JS中的模塊化

今天看YDKJS時,明白了一個以前一直疑惑的東西,那就是JS中的模塊開發,之前也使用過require.s進行模塊化的開發,但是一直不能明白其中具體的原理,看了YDKJS的講解後,真的是豁然開朗,在此記錄其中一個實例的代碼,並進行解析。

	var myModules = (function  foo(){
		var modules = {};
		function get(name){
			return modules[name];
		}
		function define(name, deps, impl){
			for(var i = 0; i < deps.length; i++){
				deps[i] =  modules[ deps[i] ];
			}
			modules[name] = impl.apply( impl, deps );
		}
		return {
			get: get,
			define: define
		}
	})();
	
	myModules.define('foo',[],function(){
		function hello(val){
			console.log('hello',val);
		}
		return {
			hello: hello
		}
	});
	myModules.define('test',['foo'],function(foo){
		function useHello(val){
			var log = "I am from test" + val;
			foo.hello( log );
		}
		return {
			useHello: useHello
		}
	})	
	myModules.get('test').useHello('hello  world');

說明:

  1. IFFE
    在整個myModules 定義的最外層,使用了立即執行函數表達式var myModules =(function foo(){...})(),這樣做的目的是爲了形成一個閉包,使用myModules 保存函數表達式function foo(){...}其中的詞法作用域。

  2. 暴露公共接口
    使用return {get: get, define: define },是爲了將公共的接口getdefine暴露出來,可以在myModules上調用這些方法。同時因爲這兩個方法的內部都使用了變量modules,所以modules就會在內存中被保存起來,類似於myModules的私有變量。

  3. 私有變量modules
    變量modules類似於myModules 的私有變量,不通過myModules 是無法訪問到這個變量的,且其會一直保存在內存中。使用這個變量,結合define方法,將所有定義的模塊都掛載到modules上。

  4. 模塊定義的方法

    function define(name, deps, impl){
    	for(var i = 0; i < deps.length; i++){
    		deps[i] =  modules[ deps[i] ];
    	}
    	modules[name] = impl.apply( impl, deps );
    }
    

    name: 定義的模塊名稱
    deps: 模塊對應的依賴
    impl: 模塊的實現

    先是使用循環根據依賴deps中的依賴模塊名,從modules中得到具體的掛載在modules上的模塊數組。
    然後是下面這句很精髓的話:
    modules[name] = impl.apply( impl, deps );
    deps依賴模塊數組作爲參數傳遞給該模塊的實現方法,並使用apply調用當前模塊的實現方法。那麼此時我們可以發現,實際掛載到modules上的並不是模塊具體的實現方法,而是模塊的返回值。但是模塊的返回值是一個對象,這個對象中的值有可以引用了模塊中定義的方法變量,如此一來,模塊暴露出來的方法和變量中涉及的詞法作用域中的變量就會被保存起來,而不會在內存中消失。其實還是應用了所謂的閉包。

  5. 模塊定義的實現
    我們來看看具體的模塊定義和實現

    myModules.define('foo',[],function(){
       function hello(val){
       	console.log('hello',val);
       }
       return {
       	hello: hello
       }
    });
    

    我們定義了foo模塊,在foo模塊中,我們暴露了hello方法,實際上此時 myModules內部的modules是這樣的

    modules = {
    	foo:{
    		hello: hello
    	}
    }
    

    可以看到modules上掛載的foo模塊實際上是foo模塊定義時的返回值,但是這個返回值中引用的方法hello此時會一直保存在內存中。

  6. 模塊間調用

    	myModules.define('test',['foo'],function(foo){
    		function useHello(val){
    			var log = "I am from test" + val;
    			foo.hello( log );
    		}
    		return {
    			useHello: useHello
    		}
    	})
    	
    	myModules.get('test').useHello('hello  world');
    

    我們在模塊test中調用了foo模塊。注意,在test模塊的實現中,我們加入了參數foo, 那麼實際定義的過程如下

    impl.apply( impl,  modules['foo'] );
    

    等價於

    impl.apply( impl,  { hello: hello } );
    

    所以,我們在test模塊的實現中,可以直接使用變量foo,就像真的在調用foo模塊一樣,然後後我們再將test模塊中的返回值掛載到modules

    modules['test'] = impl.apply( impl,  { hello: hello } );
    

    以上是我個人對於js中的模塊化的理解,如有錯誤,歡迎各位老鐵指正。

    參考資料:
    [1]: https://github.com/Alizwell/You-Dont-Know-JS/blob/1ed-zh-CN/scope %26 closures/ch5.md

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章