1、什么是模塊化
- 到底什么是模塊化呢?
- 事實(shí)上模塊化開發(fā)最終的目的是將程序劃分成一個(gè)個(gè)小結(jié)構(gòu)
- 這個(gè)結(jié)構(gòu)編寫屬于自己的邏輯代碼,有自己的作用域,不會(huì)影響別人的結(jié)構(gòu);
- 該結(jié)構(gòu)可以將自己希望暴露的變量,函數(shù)對(duì)象等導(dǎo)出給其他結(jié)構(gòu)使用。
- 也可以通過某種方式,導(dǎo)入另外結(jié)構(gòu)中的變量、函數(shù)、對(duì)象等;
- 上面說提到的結(jié)構(gòu),就模塊;按照這種結(jié)構(gòu)劃分開發(fā)程序的過程,就是模塊化開發(fā)的過程
- 無論你多么喜歡JavaScript,以及它現(xiàn)在發(fā)展的有多好,我們都需要承認(rèn)在 Brendan Eich 用了10天寫出JavaScript的時(shí)候,
它都有很多的缺陷:- 比如var定義的變量作用域問題;
- 比如JavaScript的面向?qū)ο蟛⒉荒芟癯R?guī)面向?qū)ο笳Z言一樣使用class;
- 比如JavaScript沒有模塊化的問題;
2、沒有模塊化帶來很多的問題
-
早期沒有模塊化帶來了很多的問題:比如命名沖突的問題
- 命名沖突
-
當(dāng)然,我們有辦法可以解決上面的問題:立即函數(shù)調(diào)用表達(dá)式(IIFE)
// 立即執(zhí)行函數(shù) (function() { })();
-
但是,我們其實(shí)帶來了新的問題:
- 第一,我必須記得每一個(gè)模塊中返回對(duì)象的命名,才能在其他模塊使用過程中正確的使用;
- 第二,代碼寫起來混亂不堪,每個(gè)文件中的代碼都需要包裹在一個(gè)匿名函數(shù)中來編寫;
- 第三,在沒有合適的規(guī)范情況下,每個(gè)人、每個(gè)公司都可能會(huì)任意命名、甚至出現(xiàn)模塊名稱相同的情況;
-
所以,我們會(huì)發(fā)現(xiàn),雖然實(shí)現(xiàn)了模塊化,但是我們的實(shí)現(xiàn)過于簡(jiǎn)單,并且是沒有規(guī)范的。
- 我們需要制定一定的規(guī)范來約束每個(gè)人都按照這個(gè)規(guī)范去編寫模塊化的代碼;
- 這個(gè)規(guī)范中應(yīng)該包括核心功能:模塊本身可以導(dǎo)出暴露的屬性,模塊又可以導(dǎo)入自己需要的屬性;
- JavaScript社區(qū)為了解決上面的問題,涌現(xiàn)出一系列好用的規(guī)范,接下來我們就學(xué)習(xí)具有代表性的一些規(guī)范。
3、CommonJS和Node
- 我們需要知道CommonJS是一個(gè)規(guī)范,最初提出來是在瀏覽器以外的地方使用,并且當(dāng)時(shí)被命名為ServerJS,后來為了
體現(xiàn)它的廣泛性,修改為CommonJS,平時(shí)我們也會(huì)簡(jiǎn)稱為CJS。- Node是CommonJS在服務(wù)器端一個(gè)具有代表性的實(shí)現(xiàn);
- Browserify是CommonJS在瀏覽器中的一種實(shí)現(xiàn);
- webpack打包工具具備對(duì)CommonJS的支持和轉(zhuǎn)換;
- 所以,Node中對(duì)CommonJS進(jìn)行了支持和實(shí)現(xiàn),讓我們?cè)陂_發(fā)node的過程中可以方便的進(jìn)行模塊化開發(fā):
- 在Node中每一個(gè)js文件都是一個(gè)單獨(dú)的模塊;
- 這個(gè)模塊中包括CommonJS規(guī)范的核心變量:<font color=red>exports、module.exports、require</font>;
- 我們可以使用這些變量來方便的進(jìn)行模塊化開發(fā);
- 前面我們提到過模塊化的核心是導(dǎo)出和導(dǎo)入,Node中對(duì)其進(jìn)行了實(shí)現(xiàn):
- exports和module.exports可以負(fù)責(zé)對(duì)模塊中的內(nèi)容進(jìn)行導(dǎo)出;
- require函數(shù)可以幫助我們導(dǎo)入其他模塊(自定義模塊、系統(tǒng)模塊、第三方庫模塊)中的內(nèi)容;
4、案例設(shè)定
-
我們來看一下兩個(gè)文件:
兩個(gè)文件之間互相引用
5、exports導(dǎo)出
-
注意:exports是一個(gè)對(duì)象,我們可以在這個(gè)對(duì)象中添加很多個(gè)屬性,添加的屬性會(huì)導(dǎo)出;
- exports導(dǎo)出一個(gè)對(duì)象,導(dǎo)出的內(nèi)容均為exports的屬性
-
另外一個(gè)文件中可以導(dǎo)入:
- 引入
-
上面這行完成了什么操作呢?理解下面這句話,Node中的模塊化一目了然
- 意味著main中的bar變量等于exports對(duì)象;
- 也就是require通過各種查找方式,最終找到了exports這個(gè)對(duì)象;
- 并且將這個(gè)exports對(duì)象賦值給了bar變量;
- bar變量就是exports對(duì)象了;
5.1 理解對(duì)象的引用賦值
- 對(duì)象引用賦值
5.2 畫圖解析賦值的過程
- 對(duì)象引用指向共同的堆棧
5.3 它們實(shí)際上是一個(gè)淺層拷貝
- 為了進(jìn)一步論證,bar和exports是同一個(gè)對(duì)象:
- 所以,bar對(duì)象是exports對(duì)象的淺拷貝(引用賦值);
-
淺拷貝的本質(zhì)就是一種引用的賦值而已;
淺層拷貝
6、module.exports又是什么?
- 但是Node中我們經(jīng)常導(dǎo)出東西的時(shí)候,又是通過module.exports導(dǎo)出的:
- <font color=red>module.exports和exports有什么關(guān)系或者區(qū)別呢?</font>
- 我們追根溯源,通過維基百科中對(duì)CommonJS規(guī)范的解析:
- CommonJS中是沒有module.exports的概念的;
- 但是為了實(shí)現(xiàn)模塊的導(dǎo)出,Node中使用的是Module的類,每一個(gè)模塊都是Module的一個(gè)實(shí)例,也就是
module; - 所以在Node中真正用于導(dǎo)出的其實(shí)根本不是exports,而是module.exports;
- 因?yàn)?lt;font color=red>module才是導(dǎo)出的真正實(shí)現(xiàn)者</font>;
- 但是,為什么exports也可以導(dǎo)出呢?
- 這是因?yàn)閙odule對(duì)象的exports屬性是exports對(duì)象的一個(gè)引用;
- 在node源碼內(nèi)部做了一個(gè)處理,
module.exports = exports; - 也就是說 module.exports = exports = main中的bar;
6.1 畫圖解析賦值的過程
- 對(duì)象引用
- module.exports賦值成對(duì)象,重新開辟一塊空間








