Node.js學習筆記(二):模塊

模塊是 Node.js 應用程序的基本組成部分,文件和模塊是一一對應的。一個 Node.js 文件就是一個模塊,這個文件多是 JavaScript 代碼、JSON 或者編譯過的 C/C++ 擴展。node

因爲JavaScript沒有模塊系統,因此Node.js依靠CommonJS規範自身實現了模塊系統。json

模塊的簡單使用——exports 、require 和 module

在編寫和使用每一個模塊時,Node.js都有require、exports、module三個預先定義好的變量可供使用。bootstrap

  1. exports瀏覽器

    exports對象是當前模塊的導出對象,用於導出模塊公有方法和屬性。
    事實上,exports 自己僅僅是一個普通的空對象,即 {},它專門用來聲明接口。例如:緩存

    //module.js
    exports.sayHello = function(name) { 
        console.log('Hello '    + name);
    };
  2. requireapp

    require函數用於在當前模塊中加載和使用別的模塊,傳入一個模塊名,返回一個模塊導出對象。模塊名可以使用相對路徑(以./開頭),或者是絕對路徑(以/或C:之類的盤符開頭)。另外,模塊名中的.js擴展名能夠省略。例如:函數

    //index.js
    var myModule = require('./module');
    myModule.sayHello("node");
  3. 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 = [];
}

模塊解析流程:

  1. 命令行執行主模塊

    命令行執行主模塊

    // 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();
    };
  2. 處理模塊

    Module.runMain方法會在最後執行_load靜態方法,該方法又會在分析文件名以後執行:

    //實例化Module函數
    var module = new Module(id, parent);

    並根據文件路徑緩存當前模塊對象,該模塊實例對象則根據文件名加載。

    module.load(filename);

    這時,Node.js會根據不一樣文件模塊類型的後綴名來決定加載方法。

    • js:經過fs模塊同步讀取js文件並編譯執行。
    • node:經過C/C++進行編寫的Addon。經過dlopen方法進行加載。
    • json:讀取文件,調用JSON.parse解析加載。
  3. 輸出結果

    最後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);
    };

Q&A

  1. console.log(this)在瀏覽器和Node中分別打印出什麼?

    答:顯然瀏覽器中直接打印this指向Window對象,而在Node中,咱們編寫的文件其實外面都包裹了一層函數,並且該函數執行時強制apply將this指向了module.exports,所以此處打印爲{}。

  2. 爲何require、__filename、__dirname、module、exports等幾個變量並無定義在app.js 文件中,可是這個方法卻存在的緣由。

    答:這裏提到的全部屬性均是_load方法中咱們編寫的js文件外層包裹函數提供給咱們的,所以能夠直接調用。

相關文章
相關標籤/搜索