我們已經了解到模塊模式是為單例創建私有變量和特權方法的。
一個最基本的例子:
var foo=(function(){
var something='cool',
var another=[1,2,3];
function dosomething(){
console.log(something);
}
function doAnother(){
console.log(another.join('!'));
}
return {
doSomething:doSomething,
doAnother:doAnother
}
})();
foo.doSomething(); //cool
foo.doAnother(); //1!2!3
我們將模塊函數轉換成了立即執行函數,立即調用這個函數並將返回值直接賦值給單例的模塊實例標識符。在函數內部定義了私有變量,並通過函數留下了接口,除了通過接口訪問其內部的私有變量的值,在函數外部是無法訪問它的值的,這也就保證了不會引起命名沖突,修改數據等問題,同時,模塊還可以復用,大大提高了開發效率。
其實,大多數的模塊倚賴加載器/管理器本質上都是將這種模塊定義封裝進一個友好的API(接口)。我們知道,在requirejs中,使用define([],function(){})來定一個模塊的,來看下面一段原理性代碼。
var MyModules=(function Manager(){
var moudules={}; //對象字面量
function define(name,deps,impl){
for(var i=0;i<deps.length;i++){
deps[i]=moudules[deps[i]];
}
moudules[name]=impl.apply(impl,deps);
console.log(moudules[name]);
}
function get(name){
return moudules[name];
}
return {
define:define,
get:get
};
})();
這里我們定義了一個匿名函數,並讓他立即執行,然后將返回值傳給了MyModules。
接下來我們執行這樣一段代碼:
MyModules.define('bar',[],function(){
function hello(who){
return 'Let me introduce:'+who;
}
return {
hello:hello
}
});
var bar=MyModules.get('bar');
console.log(bar.hello('nihao'));
看一下執行結果:
大致分析一下執行過程:
通過MyModules的公共接口define,同時,傳給define的第二個參數是一個空數組,所以說,for循環會跳過,這時,最關鍵的一句是
impl.apply(impl,deps)
它會將impl也就是第三個匿名函數參數的this值綁定到函數自身的作用域並同時將第二個參數deps作為函數的參數傳進去。
這時,modules對象中就多了一個bar屬性,其大致的結構跟我們開篇講的第一個例子,並沒有什么兩樣,我們看一下,其結構:
也就相當於這樣:
var bar=function(){
function hello(who){
return 'Let me introduce':+who;
}
return {
hello:hello
}
};
這時,我們再調用get函數獲取到其屬性,然后調用其hello()方法,結果也就一目了然了。
但這里,我們第二個參數是一個空數組,在requirejs中,也就相當於不倚賴任何模塊,那當他需要依賴其他模塊時,會是什么樣的情況呢,他又是如何完成其他模塊的加載的呢?
我們在代碼的基礎上,繼續增加如下代碼:
MyModules.define('foo',['bar'],function(bar){
var hungry='hippo';
function awesome(){
console.log(bar.hello(hungry).toUpperCase());
}
return {
awesome:awesome
}
});
var foo=MyModules.get('foo');
foo.awesome();
看一下其執行結果:
是按照我們預想的那樣輸出的。
我們再來分析一下執行過程:
其關鍵的步驟就是第二個參數是有一個數組值的數組,代碼將執行遍歷此數組操作,然后,將該數組的第一個值賦值為此前module對象中的bar屬性對象,所以說該數組中的值也就也就指向了moudule對象中的bar屬性對象(注意引用類型的復制問題)。
其也就當做參數傳遞到了modules對象的另外一個屬性foo之中,其實,這也就完成了requirejs中加載模塊的功能。這時,當我們引用bar模塊中的公共接口的時候,其結果也就在情理之中了。
其實,這段代碼的核心就是modules[name]=impl.apply(impl,deps)。為了模塊的定義引入了包裝函數(可以傳入任何依賴),並將其返回值,也就是模塊的API,存儲在一個根據名字來管理的模塊列表中。
以上內容,純屬個人理解,如果有不對之處,還請大家熱心指正。