模塊模式:
模塊模式是用來封裝邏輯並避免命名空間污染的好方法。使用匿名函數可以做到這一點,通常是創建一個匿名函數並立即執行它。在匿名函數中的邏輯都在閉包裏運行,爲應用中的變量提供了局部作用域和私有的運行環境:
(function(){
/* ... */
})();
在執行這個匿名函數之前,我們用一對括號()將它包起來。這樣才能讓javascirpt解釋器正確地將這段代碼解釋爲一個語句。
全局導入:
定義在模塊裏的變量都是局部變量,因此在全局命名空間中是無法訪問他們的。然而應用的全局變量仍是可用的,從模塊的內部可用很容易的訪問並操縱它們。開發者往往很難一眼看出哪個全局變量被模塊使用了,尤其是當模塊數量很多的時候。
另外,隱式的全局變量會讓程序變的更慢,因爲javascript解釋器不得不遍歷作用域鏈。
將全局對象作爲參數傳入匿名函數,可以導入我們的代碼中,這種實現比隱式的全局對象更加簡潔高效。
(function($){
/* ...*/
})(jQuery);
這個例子我們將全局對象jQuery導入我們的模塊裏,並將其重命名$。它清晰的表明這個模塊中所用到的全局變量是什麼,並且對這個全局對象的讀取速度更快。
全局導出:
(function($,exports){
exports.Foo="wem";
})(jQuery,window);
assertEqual(Foo,"wem");
這裏我們使用的變量名叫exports,用它來暴露全局變量,這樣代碼看起來乾淨易讀,可以直接看出模塊創建了哪些全局變量。
添加少量上下文
使用局部上下文是一種架構模塊很有用的方法,特別是當需要給事件註冊回調函數時。實際情況是,模塊中的上下文都是全局的,this就是window
(function(){
assertEqual(this,window);
})();
如果想自定義作用域的上下文,則需要將函數添加至一個對象中,比如:
(funcion(){
var mod={};
mod.contextFunction=function(){
assertEqual(this,mod);
};
mod.contextFunction();
})();
在 mod.contextFunction()中上下文不是全局的,而是mod對象。這時使用this就不用擔心創建全局變量了。
(funcion($){
var mod={};
mod.load=function(func){
$($.proxy(func,this));
};
mod.load(function(){
this.view=$("#view");
});
mod.assetsClick=function(){
//處理點擊
};
mod.load(funtion(){
this.view.find(".assets").click($.proxy(this.assetsClick,this));
});
})(jQuery);
這裏創建了新的load()函數來處理回調,當頁面加載後執行它,我們使用了jQuery的proxy()來確保回調函數是基於正確的上下文執行的
抽象出類庫
(function($,exports){
var mod=function(includes){
if(includes)this.include(includes);
};
mod.fn=mod.prototype;
mod.fn.proxy=function(func){
return $.proxy(func,this);
};
mod.fn.load=function(func){
$(this.proxy(func));
};
mod.fn.include=function(ob){
$.extend(this,ob);
};
exports.Controller=mod;
})(jQuery,window);
proxy()保證了函數在局部上下文中執行,對於事件回調來說是非常有用的模式。
include()函數只是給控制器添加屬性、保存類型功能的快捷方式。
代碼中將控制器掛載到export對象中,對外暴露爲全局的Controller變量
例子:根據鼠標是否移過元素來給它添加和刪除一個元素的類
(function($,Controller){
var mod=new Controller;
mod.toggleClass=function(){
this.view.toggleClass("over",e.data)
};
mod.load(function(){
this.view=$("#view");
this.view.mouseover(this.proxy(this.toggleClass),true);
this.view.mouseout(this.proxy(this.toggleClass),false);
}
);
})(jQuery,Controller)
文檔加載完後載入控制器
//使用全局對象作爲上下文而不是window對象
//用來創建全局對象
var exports=this;
(function(){
var mod={};
mod.create=function(includes){
var result=function(){
this.init.apply(this,argument);
};
result.fn=result.prototype;
result.fn.init=function(){};
result.proxy=function(func){return $.proxy(func,this);};
result.fn.proxy=result.proxy;
result.include=function(obj){$.extend(result.fn,obj);};
result.extend=function(obj){$.extend(result,obj);};
if(includes)result.include(includes);
return result;
};
exports.Controller=mod;
})(jQuery);
現在我們可以使用新的Controller.create()函數來創建控制器,傳入一個包含實例屬性的對象直接量
注意這裏整個控制器都被包裝在jQuery(function(){/* ... */})中,這是jQuery.ready()的另一種寫法,
這句話的意思就是在頁面DOM節點構建完成後才執行初始化的動作加載控制器。
jQuery(function($){
var ToggleView = Controller.create({
init: function(view){
this.view = $(view);
this.view.mouseover(this.proxy(this.toggleClass), true);
this.view.mouseout(this.proxy(this.toggleClass), false);
},
this.toggleClass: function(e){
this.view.toggleClass("over", e.data);
}
});
// Instantiate controller, calling init()
new ToggleView("#view");
});