立即執行函數寫法
使用”立即執行函數”(Immediately-Invoked Function Expression,IIFE),可以達到不暴露私有成員的目的。
var module1 = (function(){
var _count = ;
var m1 = function(){
//...
};
var m2 = function(){
//...
};
return {
m1 : m1,
m2 : m2
};
})();
使用上面的寫法,外部代碼無法讀取内部的_count變量。
放大模式
如果一個子產品很大,必須分成幾個部分,或者一個子產品需要繼承另一個子產品,這時就有必要采用”放大模式”(augmentation)。
var module1 = (function (mod){
mod.m3 = function () {
//...
};
return mod;
})(module1);
上面的代碼為module1子產品添加了一個新方法m3(),然後傳回新的module1子產品。
寬放大模式(Loose augmentation)
在浏覽器環境中,子產品的各個部分通常都是從網上擷取的,有時無法知道哪個部分會先加載。如果采用上一節的寫法,第一個執行的部分有可能加載一個不存在空對象,這時就要采用”寬放大模式”。
var module1 = ( function (mod){
//...
return mod;
})(window.module1 || {});
與”放大模式”相比,"寬放大模式"就是”立即執行函數”的參數可以是空對象。
輸入全局變量
獨立性是子產品的重要特點,子產品内部最好不與程式的其他部分直接互動。
為了在子產品内部調用全局變量,必須顯式地将其他變量輸入子產品。
var module1 = (function ($, YAHOO) {
//...
})(jQuery, YAHOO);
上面的module1子產品需要使用jQuery庫和YUI庫,就把這兩個庫(其實是兩個子產品)當作參數輸入module1。這樣做除了保證子產品的獨立性,還使得子產品之間的依賴關系變得明顯。
JQuery1.5中最末一句
window.jQuery = window.$ = jQuery;
AMD
AMD是”Asynchronous Module Definition”的縮寫,意思就是”異步子產品定義”。它采用異步方式加載子產品,子產品的加載不影響它後面語句的運作。所有依賴這個子產品的語句,都定義在一個回調函數中,等到加載完成之後,這個回調函數才會運作。
子產品的加載
使用require.config()方法,我們可以對子產品的加載行為進行自定義。require.config()就寫在主子產品(main.js)的頭部。參數就是一個對象,這個對象的paths屬性指定各個子產品的加載路徑。
require.config({
paths: {
"jquery": "jquery.min",
"underscore": "underscore.min",
"backbone": "backbone.min"
}
});
上面的代碼給出了三個子產品的檔案名,路徑預設與main.js在同一個目錄(js子目錄)。如果這些子產品在其他目錄,比如js/lib目錄,則有兩種寫法。一種是逐一指定路徑。
require.config({
paths: {
"jquery": "lib/jquery.min",
"underscore": "lib/underscore.min",
"backbone": "lib/backbone.min"
}
});
另一種則是直接改變基目錄(baseUrl)。
require.config({
baseUrl: "js/lib",
paths: {
"jquery": "jquery.min",
"underscore": "underscore.min",
"backbone": "backbone.min"
}
});
如果某個子產品在另一台主機上,也可以直接指定它的網址,比如:
require.config({
paths: {
"jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min"
}
});
require.js要求,每個子產品是一個單獨的js檔案。這樣的話,如果加載多個子產品,就會發出多次HTTP請求,會影響網頁的加載速度。是以,require.js提供了一個優化工具,當子產品部署完畢以後,可以用這個工具将多個子產品合并在一個檔案中,減少HTTP請求數。
AMD子產品的寫法
require.js加載的子產品,采用AMD規範。也就是說,子產品必須按照AMD的規定來寫。
具體來說,就是子產品必須采用特定的define()函數來定義。如果一個子產品不依賴其他子產品,那麼可以直接定義在define()函數之中。
假定現在有一個math.js檔案,它定義了一個math子產品。那麼,math.js就要這樣寫:
// math.js
define(function (){
var add = function (x,y){
return x+y;
};
return {
add: add
};
});
如果這個子產品還依賴其他子產品,那麼define()函數的第一個參數,必須是一個數組,指明該子產品的依賴性。
define(['myLib'], function(myLib){
function foo(){
myLib.doSomething();
}
return {
foo : foo
};
});
加載方法如下:
// main.js
require(['math'], function (math){
alert(math.add(,));
});
主子產品的寫法
require(['jquery', 'underscore', 'backbone'], function ($, _, Backbone){
// some code here
});
加載非規範的子產品
理論上,require.js加載的子產品,必須是按照AMD規範、用define()函數定義的子產品。但是實際上,雖然已經有一部分流行的函數庫(比如jQuery)符合AMD規範,更多的庫并不符合。那麼,require.js是否能夠加載非規範的子產品呢?
回答是可以的。
這樣的子產品在用require()加載之前,要先用require.config()方法,定義它們的一些特征。
舉例來說,underscore和backbone這兩個庫,都沒有采用AMD規範編寫。如果要加載它們的話,必須先定義它們的特征。
require.config({
shim: {
'underscore':{
exports: '_'
},
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
}
}
});
require.config()接受一個配置對象,這個對象除了有前面說過的paths屬性之外,還有一個shim屬性,專門用來配置不相容的子產品。具體來說,每個子產品要定義(1)exports值(輸出的變量名),表明這個子產品外部調用時的名稱;(2)deps數組,表明該子產品的依賴性。
比如,jQuery的插件可以這樣定義:
shim: {
'jquery.scroll': {
deps: ['jquery'],
exports: 'jQuery.fn.scroll'
}
}
require.js插件
require.js還提供一系列插件,實作一些特定的功能。
domready插件,可以讓回調函數在頁面DOM結構加載完成後再運作。
require(['domready!'], function (doc){
// called once the DOM is ready
});
text和image插件,則是允許require.js加載文本和圖檔檔案。
define([
'text!review.txt',
'image!cat.jpg'
],
function(review,cat){
console.log(review);
document.body.appendChild(cat);
}
);