其實作為一個秋招狗,前端還沒寫夠一年就來大談特談這些歷史性問題齐遵,的確是不夠格的戒良,但是這又是學(xué)習(xí)前端學(xué)習(xí)者無法避免的一個問題体捏,加上面試提問頗多,于是來總結(jié)一二糯崎。
站在巨人的肩膀上几缭。
參考資料:
Javascript模塊化編程(一):模塊的寫法
Javascript模塊化歷程
模塊
模塊就是實現(xiàn)特定功能的一組方法。
最開始筆者學(xué)習(xí)js的時候沃呢,新建一個js文件年栓,直接在里面寫邏輯代碼,然后script標(biāo)簽引入即可薄霜,一兩個小網(wǎng)頁是無妨的某抓,但是隨后后面邏輯越來越復(fù)雜,代碼量越來越大惰瓜,可復(fù)用的要求越來越多否副,筆者也是看著越來越混亂的js文件焦灼無比。
畢竟js最初只是作為一門“玩具語言”闖進(jìn)開發(fā)者的視野崎坊,甚至這門語言僅僅花了不到一個月的時間就被創(chuàng)造了出來备禀,我們?nèi)缃窨匆姷膉s,是開發(fā)者與互聯(lián)網(wǎng)不斷的磨合妥協(xié)下的js.
所以開發(fā)者不得不使用軟件工程的方法奈揍,管理網(wǎng)頁的業(yè)務(wù)邏輯曲尸。
但是,Javascript不是一種模塊化編程語言男翰,它不支持"類"(class)另患,更遑論"模塊"(module)了。(正在制定中的ECMAScript標(biāo)準(zhǔn)第六版奏篙,將正式支持"類"和"模塊"柴淘,但還需要一段時間才能投入實用迫淹。)
“Don`t repeat yourself”是我們的準(zhǔn)則,如何更好的去實踐這條準(zhǔn)則就是我們今天要講的內(nèi)容为严。
函數(shù)封裝
直接在頁面中寫邏輯代碼是我最初的做法敛熬,后來慢慢地學(xué)會了將重復(fù)的代碼封裝至一個函數(shù)之內(nèi),需要時調(diào)用函數(shù)即可第股。函數(shù)一個功能就是實現(xiàn)特定邏輯的一組語句打包应民,而且JavaScript的作用域就是基于函數(shù)的,所以把函數(shù)作為模塊化的第一步是很自然的事情夕吻。
只要把不同的函數(shù)(以及記錄狀態(tài)的變量)簡單地放在一起诲锹,就算是一個模塊。
function fn1(){
statement
}
function fn2(){
statement
}
這樣在需要的以后加載函數(shù)所在文件涉馅,調(diào)用函數(shù)就可以了归园。
這種做法的缺點(diǎn)很明顯:污染了全局變量,無法保證不與其他模塊發(fā)生變量名沖突稚矿,而且模塊成員之間沒什么關(guān)系庸诱。
對象
我們需要一個方法將全局變量減少,于是對象寫法很自然的就出現(xiàn)了晤揣。
var myModule = {
fn1: function(){
statement
},
fn2: function(val){
statement
}
}
世界美好了那么一點(diǎn)點(diǎn)桥爽。
這樣我們在希望調(diào)用模塊的時候引用對應(yīng)文件,然后
myModule.fn1();
但是我們看看下面這段代碼昧识。
var myModule = {
var1: 1,
var2: 2,
fn1: function(){
return var1;
},
fn2: function(val){
var2 = val;
}
}
不知道大家發(fā)現(xiàn)沒有钠四,我明明可以直接通過
myModule.var1;
直接獲取var1,那我還要調(diào)用fn1去return干嘛跪楞,簡直奇怪缀去。
沒錯,看似不錯的解決方案习霹,但是也有缺陷朵耕,這樣的寫法會暴露所有模塊成員,內(nèi)部狀態(tài)可以被外部改寫淋叶,毫無封裝型可言。
在別的語言中可以很輕易的實現(xiàn)私有變量伪阶,例如java的private關(guān)鍵字煞檩,但是js需要通過別的手段來實現(xiàn)。
我們希望外部不能夠如此隨意的訪問或者修改某一個變量栅贴,只能通過我們規(guī)定的特權(quán)方法去訪問模塊的變量斟湃,而無法直接改寫內(nèi)部狀態(tài),于是有了閉包檐薯。
閉包
沒錯凝赛,閉包又出現(xiàn)了注暗,當(dāng)初學(xué)習(xí)閉包的時候,網(wǎng)上的種種文章墓猎,描述了閉包的定義捆昏,閉包的特點(diǎn),以及如何實現(xiàn)閉包毙沾,但是骗卜,他們就是沒有告訴你閉包主要是拿來做什么的,在我看來左胞,閉包就是模塊化的基礎(chǔ)寇仓!
前面我們提到過JavaScript的作用域就是基于函數(shù)的,我們可以通過這一點(diǎn)將想要隱藏起來的變量置于函數(shù)作用域中烤宙,根據(jù)作用域鏈訪問的原則遍烦,外層是無法訪問到該變量的,
看下面代碼
var module1 = (function(){
var var1 = 0;
var fn1 = function(){
//...
};
var fn2 = function(){
//...
};
return {
fn1 : fn1,
fn2 : fn2
};
})();
這里是一個立即執(zhí)行函數(shù)躺枕,return一個對象乳愉,該對象內(nèi)部的函數(shù)是可以直接訪問到var1的(閉包),而外部再也不能輕易的去訪問var1了屯远,只能通過module1的方法去訪問蔓姚,世界又美好了一點(diǎn)。
PS:我一直覺得立即執(zhí)行函數(shù)的本質(zhì)就只是為了立即執(zhí)行而已慨丐,不理解為什么還特地產(chǎn)生了一個概念坡脐,如果您有更好的解答,請寫下您的評論房揭。
module1就是Javascript模塊的基本寫法备闲。
當(dāng)然還有類似于jq的那種寫法,
var module1 = (function ($, YAHOO) {
//...
})(jQuery, YAHOO);
獨(dú)立性是模塊的重要特點(diǎn)捅暴,模塊內(nèi)部最好不與程序的其他部分直接交互恬砂。
為了在模塊內(nèi)部調(diào)用全局變量,必須顯式地將其他變量輸入模塊蓬痒。
輸入全局變量泻骤,這樣做除了保證模塊的獨(dú)立性,還使得模塊之間的依賴關(guān)系變得明顯梧奢。
看懂了上面的狱掂,這一個應(yīng)當(dāng)很容易理解。
下集預(yù)告:AMD/CMD/Commonjs/ES6
最近一直在總結(jié)校招重點(diǎn)亲轨,而那些重點(diǎn)基本是搜集別人的文章里面的趋惨,發(fā)在簡書上未免有拿別人的文章騙贊之嫌。
所以有面試的小伙伴們可以去我的博客看看惦蚊,相互交流器虾,共同提升讯嫂。