node.js模塊簡明詳解

什么是模塊?

我們先簡單描述下模塊的特點,模塊是一個獨立的完成某些功能的單位,它應該具有抽象性、封裝性(接口),例如u盤就是一個模塊,為什么這么所呢,
1.獨立性:我們將文件存入u盤,它是可以獨立完成文件的存儲功能。
2.抽象性:u盤就是將便攜式文件存儲的這項常用、公用的功能提取出來的產物。
3.封裝性:u盤里面有哪些零件,往往從它外面是看不見的,這里這么說會有些歧義,嚴謹點說就是u盤里面的那些我們不需要關心的零件,u盤都包裝了起來,對外只會留給我們一個使用的usb接口(接口在封裝性中非常重要)

js模塊

js的模塊化的發展是一個演進的過程,目的是為了解決命名沖突、文件依賴等問題,一些原始的寫法、模塊化的工具sea.js、requireJs、再到webpack等工具這些模塊化的知識大家可以參考其他資料,這里筆者不贅述,只說關鍵的、跟要說的node.js模塊相關的,先來看代碼

    var foo=(function(){
        var bar=123;
        var add=function(v1,v2){
           return v1+v2;
        }
        return {
           add:add
        }
   )()

上面的代碼就是js的一個模塊,獨立完成某些功能和抽象,這里不用說了,就是一個簡單add方法,我們重點說下封裝,這里使用return的方式對外開放接口, 而在函數里面的其他代碼例如bar,就屬于被函數封裝了。那么為什么要說這一段js代碼,其實不管AMD、CMD、commonJs規范或者es6的模塊,它們的實現的核心就是函數自調用的這種封裝。

node.js模塊

首先說node.js模塊是采用CommonJS模塊規范,那么和上面js模塊有什么樣的關系,先不用著急,我們先看下下面這個圖:


F8CAB508-5665-468D-AB99-C38B9ECE3CBB.png

這是在Chrome控制臺上定義一個foo,window對象就會多一個屬性foo,我們在頁面上加載的js代碼都有這樣的特點,因為window是全局對象。
對比一下node.js的代碼,node.js也有一個全局對象global,會不會和window一樣呢,我們看下下面的代碼:

foo = 123;
var bar = 456;
console.log('foo--->',global.foo); 
console.log('bar--->',global.bar);

輸出結果:


D4DDCDCA-8919-446F-B437-BAC7B1B565C2.png

在一個js里面去定義全局變量foo,foo會變成global的屬性,而bar沒有,bar變成這個js私有的了,就如同下面這張圖:


47BD6253-3382-4A05-97EC-D395B0B2C227.png

在node.js里面,我們把一個js就看成是一個模塊,它具有封裝性,在這個模塊里面的代碼(全局變量除外)都是私有的,如果想要被外部調用,那就需要exports與module.exports曝露出去,并且用require去接收。

module.exports與exports

一個node.js模塊中也就是一個js中(下面我們都用一個模塊代替一個js),module.exports用法如下

//用法1
//直接賦值,可以賦數字、字符串、數組、對象、函數
//注意:一個模塊中module.exports只能賦值一次
module.exports=123;
//用法2
//屬性賦值,可以多次賦值,賦數字、字符串、數組等都可以
module.exports.foo=123;
module.exports.bar=456;

exports的用法如下

//用法
//exports只能通過屬性的方式曝露,曝露數字、字符串、數組等都可以
exports.foo=123;

require

有了代碼的曝露,那么也就有代碼的引入,require就是用來加載模塊的,這里我們先不談模塊化系統,只說加載一個簡單模塊,module.exports與require如下

//1.js
module.exports=‘囧’
//2.js
var foo=require(‘./1’);
console.log(foo); //輸出囧

exports與require如下

//3.js
exports.bar=‘囧’
//4.js
var foo=require(‘./1’);
console.log(foo.bar); //輸出囧

通過require得到的就是module.exports,永遠是module.exports,那exports呢?module.exports與exports的區別是什么呢?,exports是module.exports的別名,可以理解為 var exports=module.exports,所以exports只能用于屬性的賦值,應用場景也是不同的,如果我只想從一個模塊中曝露出一個字符串或者一個數字,那要用module.exports,如果用exports則會向上面的4.js那樣需要通過屬性的形式取,顯的很繁瑣,如果是用來曝露多個屬性,雖然module.exports可以用屬性曝露,最好還是用exports,因為寫起來簡單...對就是這個原因----簡單,如下

//5.js
module.exports.foo=123;
module.exports.bar=123;
//6.js
exports.foo=123;
exports.bar=123;

兩種寫法都可以,所以一次性曝露用module.exports,如果是曝露多個屬性、方法、字符串等用exports比較簡單。

模擬require

上面我們還遺留了兩個問題,1. var bar = 456;這個bar是怎么在一個模塊中變成私有的?2.為什么exports是module.exports的別名?
那么下面我們簡單模擬下require方法,如下

var fs = require('fs');
//模擬require方法
var myRequire = function (path) {
    var Module = function () {
        this.exports = {}
    }
    var code = fs.readFileSync(path);
    //包裝代碼
    var packageCode = `(function(exports ,module) {${code}  return module.exports})`;
    console.log(packageCode);
    //獲取要執行的方法
    var result = eval(packageCode);
    console.log(result);
    var module=new Module();
    //將module.exports當實參傳給exports
    return result(module.exports,module);
}
//調用
var foo = myRequire('./foo.js');
console.log(foo);

首先我們用fs模塊將要引入的foo.js的代碼讀出來,然后包裝成下面的代碼

(function(exports ,module) {
//foo.js里面的代碼
 return module.exports})

現在我們就清楚了為什么一個js里面的代碼是具有封裝性的了,用的是js模塊那提到過的函數函數自調用的方式封裝的。

 return result(module.exports,module);

注意我們去調用result這個方法,result指向的就是(function(exports ,module) {//foo.js里面的代碼 return module.exports})這個方法,那么這個方法的形參就是exports、module,實參是module.exports、module,也就是說我們在代碼里面使用的exports,其實是指向的module.exports。

var Module = function () {
        this.exports = {}
    }
  var module=new Module();

這里我們看到一個module對象,這個對象是用來存儲每一個模塊的信息,我們可以在模塊的代碼里面打印下module看一下

console.log(module);
340235D0-BB0D-44F9-833E-019FF690EC82.png

如上圖,module里面的exports默認是一個空對象,和我們模擬的寫法一致,這也就是我們可以直接是引用module.exports.屬性曝露接口的原因,parent、children是用來存被引用和引用的模塊的,paths這是npm查找模塊的路徑,每一個被node加載的模塊都有唯一的一個module存儲著這個模塊的信息,也說明module是動態產生的。筆者在這里只談基礎的模塊,至于模塊化系統,請參考其他文章。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,406評論 6 538
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,034評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,413評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,449評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,165評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,559評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,606評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,781評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,327評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,084評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,278評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,849評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,495評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,927評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,172評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,010評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,241評論 2 375

推薦閱讀更多精彩內容

  • Node.js是目前非常火熱的技術,但是它的誕生經歷卻很奇特。 眾所周知,在Netscape設計出JavaScri...
    w_zhuan閱讀 3,629評論 2 41
  • topics: 1.The Node.js philosophy 2.The reactor pattern 3....
    宮若石閱讀 1,109評論 0 1
  • Node.js是目前非常火熱的技術,但是它的誕生經歷卻很奇特。 眾所周知,在Netscape設計出JavaScri...
    Myselfyan閱讀 4,084評論 2 58
  • 1 Node.js模塊的實現 之前在網上查閱了許多介紹Node.js的文章,可惜對于Node.js的模塊機制大都著...
    zlx_2017閱讀 1,274評論 0 1
  • 今天辦身份證的路上,媽媽跟一個熟人聊到還在上學的弟弟,說了句:“現在才上小學,還不知道什么時候能熬出頭”。“熬出頭...
    Miranda明霞閱讀 1,220評論 0 0