模塊是 Node.js 應用程序的基本組成部分,文件和模塊是一一對應的。一個 Node.js 文件就是一個模塊,這個文件多是 JavaScript 代碼、JSON 或者編譯過的 C/C++ 擴展。node
因爲JavaScript沒有模塊系統,因此Node.js依靠CommonJS規範自身實現了模塊系統。json
在編寫和使用每一個模塊時,Node.js都有require、exports、module三個預先定義好的變量可供使用。bootstrap
exports瀏覽器
exports
對象是當前模塊的導出對象,用於導出模塊公有方法和屬性。
事實上,exports 自己僅僅是一個普通的空對象,即 {},它專門用來聲明接口。例如:緩存
//module.js exports.sayHello = function(name) { console.log('Hello ' + name); };
requireapp
require
函數用於在當前模塊中加載和使用別的模塊,傳入一個模塊名,返回一個模塊導出對象。模塊名可以使用相對路徑(以./開頭),或者是絕對路徑(以/或C:之類的盤符開頭)。另外,模塊名中的.js擴展名能夠省略。例如:函數
//index.js var myModule = require('./module'); myModule.sayHello("node");
moduleui
經過module對象能夠訪問到當前模塊的一些相關信息,但最多的用途是覆蓋 exports。例如模塊導出對象默認是一個普通對象,若是想改爲一個函數的話:this
//module.js module.exports = function(name) { console.log('Hello ' + name); }; //index.js var sayHello = require('./module'); sayHello("node");
Node.js的模塊分爲兩類,一類爲原生(核心)模塊,一類爲文件模塊。原生模塊在Node.js源代碼編譯的時候編譯進了二進制執行文件,加載的速度最快。另外一類文件模塊是動態加載的,加載速度比原生模塊慢。prototype
加載文件模塊的工做,主要由原生模塊module來實現和完成,該原生模塊在啓動時已經被加載,進程直接調用到runMain靜態方法。
Module源碼:
function Module(id, parent) { this.id = id; this.exports = {}; this.parent = parent; if (parent && parent.children) { parent.children.push(this); } this.filename = null; this.loaded = false; this.children = []; }
模塊解析流程:
命令行執行主模塊
命令行執行主模塊
// bootstrap main module. Module.runMain = function() { // Load the main module--the command line argument. Module._load(process.argv[1], null, true); // Handle any nextTicks added in the first tick of the program process._tickCallback(); };
處理模塊
Module.runMain方法會在最後執行_load靜態方法,該方法又會在分析文件名以後執行:
//實例化Module函數 var module = new Module(id, parent);
並根據文件路徑緩存當前模塊對象,該模塊實例對象則根據文件名加載。
module.load(filename);
這時,Node.js會根據不一樣文件模塊類型的後綴名來決定加載方法。
輸出結果
最後js文件形式的模塊會變成如下形式的內容:
(function (exports, require, module, __filename, __dirname) { var circle = require('./circle.js'); console.log('The area of a circle of radius 4 is ' +circle.area(4)); });
因此此時,主模塊內可使用exports, require, module等變量了,而其餘模塊又會經過require引進主模塊,require方法會同runMain同樣調用_load靜態方法,以此類推。
require源碼:
// 傳入模塊路徑做爲參數. 返回 模塊的exports屬性. Module.prototype.require = function(path) { assert(path, 'missing path'); assert(typeof path === 'string', 'path must be a string'); return Module._load(path, this, /* isMain */ false); };
console.log(this)在瀏覽器和Node中分別打印出什麼?
答:顯然瀏覽器中直接打印this指向Window對象,而在Node中,咱們編寫的文件其實外面都包裹了一層函數,並且該函數執行時強制apply將this指向了module.exports,所以此處打印爲{}。
爲何require、__filename、__dirname、module、exports等幾個變量並無定義在app.js 文件中,可是這個方法卻存在的緣由。
答:這裏提到的全部屬性均是_load方法中咱們編寫的js文件外層包裹函數提供給咱們的,所以能夠直接調用。