2012年11月7日 星期三

[宅] javascript的模組化設計

最近看到幾篇相當棒的javascript模組化教學文章,在這邊收錄一下:
Javascript模块化编程(一):模块的写法
Javascript模块化编程(二):AMD规范
如何设计大规模 JavaScript 应用 (Part 1 - 概览)
Immediately-Invoked Function Expression (IIFE)

來為所學不精的自己做個學習筆記.....Q__Q


Immediately-Invoked Function Expression(IIFE)

IIFE其實就是像這樣的寫法:
var mod = (function(){var d=0; alert(d);})(); //typeof(mod)為"undifined"
也可以是
var mod = (function(){var d=0; alert(d);}()); //typeof(mod)為"undifined"
上面的寫法跟下面是不一樣的
var mod = (function(){var d=0; alert(d);}); //typeof(mod)為"function"

上面的寫法會直接執行function,但也就只是這樣而已,什麼都沒留下。若只是單純的IIFE其實只要這樣寫就行了:
(function(){var d=0; alert(d);})();



javascript的模組化

但既然要取得一個module我們當然要把內容給keep住,所以得要把需要的attribute包成object後return回來,所以改寫成這樣:
var mod = (function(){
    var d=0;
    var foo=function(){alert(d);};
    return {foo: foo}
})();
這樣我們就可以用mod.foo()來呼叫這個函數,也會alert 0,代表mod.foo()是找得到d的。
但是為什麼不直接向下面這樣定義object就好,還要這麼大費周章呢?
mod={foo: function(){alert(d);}, d:0}
主因就是用這樣的寫的話外部就能看到、也能改動mod.d,而模組化的mod中只找的到mod.foo,從外部是看不到d的。模組化後的物件可以含有像是java的private屬性~所以要讀寫這些屬性就得再寫getter和setter啦。



模組化的規範(CommonJS)

引述 Cat Chen大大的說法:一個模組系統至少要能解決兩個問題:dependencies的載入,以及private、public導出的分別。而CommonJS就是針對server side的javascript的相關規範,其中除了module的規範外還訂定了如Binary strings and buffers、Charset encodings、System process arguments、File system interface、Socket streams、Web server gateway interface、
Local and remote packages and package management等相關規範。更多SPEC可看官方文件:http://commonjs.org/impl/
其實Node.js就是遵照CommonJS的規範,從以下的官方範例就可以看出node.js的影子:

math.js
exports.add = function() {
    var sum = 0, i = 0, args = arguments, l = args.length;
    while (i < l) {
        sum += args[i++];
    }
    return sum;
};

increment.js
var add = require('math').add;
exports.increment = function(val) {
    return add(val, 1);
};

program.js
var inc = require('increment').increment;
var a = 1;
inc(a); // 2
module.id == "program";

很明顯的,在CommonJS訂定的module架構中,require到的是module裡所定義的export。



瀏覽器端的模組化規範 - AMD

但是在前面提到的CommonJS規範並不適用於瀏覽器環境,主因是在本機端是從磁碟機載入相關lib,但在client端可是得一個個下載,在同步的情況下必須等到載入完後才會進行接下來的程式,所以頁面看起來就會像是短暫的當機一樣。
這也是為什麼在client端必須以非同步的方式載入(就是同時載入相關module)。但是以非同步的方式載入又會出現一個問題,如果前面的module還沒載入完畢,就被後面的程式所呼叫了呢?
"Asynchronous Module Definition"(AMD)就是為了避免這些問題的出現所訂定的規範。
不同於CommonJS的require,AMD中的require()需要兩個參數:
require([module], callback);
而目前最常見的AMD實作就是require.js了,可以去官網研究一下。