JavaScript模塊的前世今生

2018-06-16 20:28 更新

如今JavaScript模塊化編程的概念已經(jīng)普及開來,一提起模塊化,大家想到的可能是AMD,CMD,requirejs或seajs。其實(shí)還有很多其他的概念。本文將會陳述下JavaScript模塊的前世今生。

眾所周知,JavaScript由于歷史的原因并沒有模塊的概念,自從ajax帶來了web2.0概念后,js代碼已經(jīng)和以前大不相同了,2009年HTML5興起后,前端代碼的行數(shù)已經(jīng)呈現(xiàn)井噴式發(fā)展,隨著代碼量的增加,模塊的缺失的缺點(diǎn)日益凸顯,Javascript社區(qū)做了很多探索。

模塊的定義

模塊并非js語言獨(dú)創(chuàng),顯然是借鑒其他語言的,下面是百度百科對模塊的定義:

模塊,又稱構(gòu)件,是能夠單獨(dú)命名并獨(dú)立地完成一定功能的程序語句的集合(即程序代碼和數(shù)據(jù)結(jié)構(gòu)的集合體)

從中提煉出幾個關(guān)鍵字就是,獨(dú)立,集合,完成一定功能。

上面的提煉,再從其他語言的實(shí)現(xiàn)中借鑒下,總結(jié)起來,我們期待的模塊有如下特性:

  • 獨(dú)立性——能夠獨(dú)立完成一個功能,不受外部環(huán)境的影響
  • 完整性——完成一個特定功能
  • 集合性——一組語句的集合
  • 依賴性——可以依賴已經(jīng)存在的模塊
  • 被依賴——可以被其他模塊依賴

其實(shí)我們想要的就是一個獨(dú)立的模塊,并能引用依賴,及被依賴。

C語言的庫和頭文件(include),java的包(import)。這在其他語言中都是原生支持的特性,在js中卻是沒有的。

原始寫法

如果僅從定義入手,那么一個函數(shù)即可成為一個模塊(獨(dú)立,集合,完成一個功能),那我們就先從最原始的探索開始,也許不經(jīng)意間,我們早已在使用模塊了。

//最簡單的函數(shù),可以稱作一個模塊
function add(x, y) {
	return x + y;
}

稍微了解點(diǎn)javascript基礎(chǔ)的人都知道js中能創(chuàng)建作用域的就是函數(shù)(ES6之前),總結(jié)下社區(qū)的探索,對模塊的模擬大概如下:

(function (mod, $, _) {
	mod.add = ***;
	mod.sub = ***;
}((window.mod = window.mod || {}), jQuery, Underscore));

上面的mod模塊不會重復(fù)定義,可自由定義依賴。

99%的人思想會止步于此,但這種實(shí)現(xiàn)其實(shí)并不完美,仍然需要手動維護(hù)依賴的順序。典型的場景就是上面的jquery必須先于我們的代碼引入,不然會報(bào)引用錯誤,這顯然不是我們想要的。

我在寫Painter的時(shí)候,曾經(jīng)手動維護(hù)幾十個script之間的先后順序,這種感覺很虐心,最后想加個新script很容易報(bào)錯。下面介紹的

YUI

前段時(shí)間雅虎宣布YUI不再更新了,很是傷感,最早接觸模塊的概念,當(dāng)屬YUI了,當(dāng)然不是YUI2了。

YUI3經(jīng)過全新設(shè)計(jì),使用了沙箱模式 + 命名空間的方式,并有了模塊的概念。

例如在YUI3中想使用一個模塊,需要如下這樣:

//使用node模塊,node模塊會作為參數(shù)傳入
YUI().use('node', function (Y) {
	///***
}

YUI的模塊化已經(jīng)做的很好了,但對于僅想使用模塊的人,要引入YUI確實(shí)有點(diǎn)太重了。

CMD(Common Module Definition)

說道CMD就不能不提commonjs,提到commonjs就不能不提node。

CMD規(guī)范參照commonjs中的方式,定義模塊的方式如下:

define(function(require, exports, module) {

  // The module code goes here
});

一個文件就是一個模塊,文件名就是模塊的名字,使用模塊的方法也和commonjs中一致,只需require就好了,模塊名字可省略后綴。

//使用event.js模塊
var ec = require('event');

CMD的典型實(shí)現(xiàn)就是seajs,應(yīng)用的很廣泛。

AMD(Asynchronous Module Definition)

[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD-(%E4%B8%AD%E6%96%87%E7%89%88)是異步模塊定義,特別適合在瀏覽器端使用,其規(guī)范和CMD是很像的,AMD規(guī)范中定義模塊的方式如下:

define(id?, dependencies?, factory);

同CMD一樣,一個文件即一個模塊,模塊的使用方法如下:

define(["beta"], function (beta) {
	bata.***//調(diào)用模塊
});

AMD主張依賴注入,這點(diǎn)和CMD不同(以來查找)。

AMD也支持已CMD的方式來使用依賴。

AMD的典型實(shí)現(xiàn)有requireJS,modJSlodJS。

KMD

KMD是kissy中提出來的,是kissy自己的一套模塊化方案,具體我也不是很清楚,感興趣的同學(xué)可自行搜索相關(guān)資料。

有一次同事@eric曦堯無意說起,KMD的意思是 kill amd and cmd,當(dāng)時(shí)覺得好高打上的名字(/ □ \)。

ES6

ES6帶來了語言層面的模塊化支持,規(guī)范方面見這里,文檔方面見這里。

我們現(xiàn)在期待的就是ES6規(guī)范快點(diǎn)塵埃落定(據(jù)說今年夏天),現(xiàn)在還處于草案狀態(tài),還有瀏覽器廠商們的大力支持,還有就是在國內(nèi)盡快普及開來。

UMD

UMD的全稱是Universal Module Definition。和它名字的意思一樣,這種規(guī)范基本上可以在任何一個模塊環(huán)境中工作。

一段典型的UMD代碼如下所示:

(function (root, factory) {
    var Data = factory(root);
    if ( typeof define === 'function' && define.amd) {
        // AMD
        define('data', function() {
            return Data;
        });
    } else if ( typeof exports === 'object') {
        // Node.js
        module.exports = Data;
    } else {
        // Browser globals
        var _Data = root.Data;
        
        Data.noConflict = function () {
            if (root.Data === Data) {
                root.Data = _Data;
            }
            
            return Data;
        };
        root.Data = Data;
    }
}(this, function (root) {
	var Data = ...
	//自己的代碼
	return Data;
}));

這是出自data.js中的一部分代碼,其原理就是做個判斷,不同的環(huán)境進(jìn)行不同的處理。

我已將UMD應(yīng)用到自己的項(xiàng)目中,瞬間感覺高大上了不少:-)。

總結(jié)

比較成氣候的模塊化方案,當(dāng)屬AMD和CMD,網(wǎng)上關(guān)于二者比較的文章甚多,很難評價(jià)誰好誰壞,當(dāng)下開來AMD的使用范圍似乎更廣些,而CMD的本土化方面做的更好。

這些模塊化的探索,使前端工程化成為了可能,可以說沒有模塊,工程化更無從彈起,本文總結(jié)了大家在模塊化方面的一些探索,下一篇文章將重點(diǎn)介紹下lodJS(一款基于AMD的模塊加載器)的實(shí)踐和原理。

參考資料

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號