nodejs的模組系統(執行個體分析exprots和module.exprots)

來源:互聯網
上載者:User

標籤:擷取   csdn   tty   boolean   動態載入   講解   expr   工作   關閉   

前言:工欲善其事,必先利其器。模組系統是nodejs組織管理代碼的利器也是調用第三方代碼的途徑,本文將詳細講解nodejs的模組系統。在文章最後執行個體分析一下exprots和module.exprots。

nodejs的模組什麼是模組?

node.js通過實現CommonJS的Modules/1.0標準引入了模組(module)概念,模組是Node.js的基本組成部分.一個node.js檔案就是一個模組,也就是說檔案和模組是一一對應的關係.這個檔案可以是JavaScript代碼,JSON或者編譯過的C/C++擴充.

Node.js的模組分為兩類,一類為原生(核心)模組,一類為檔案模組。

在檔案模組中,又分為3類別模組。這三類檔案模組以尾碼來區分,Node.js會根據尾碼名來決定載入方法。

  • .js。通過fs模組同步讀取js檔案並編譯執行。
  • .node。通過C/C++進行編寫的Addon。通過dlopen方法進行載入。
  • .json。讀取json檔案,調用JSON.parse解析載入。

Node提供了exports和require兩個對象,其中exports是模組公開的介面,require用於從外部擷取一個模組介面,即所擷取模組的exports對象


require和exports
require

require函數用於在當前模組中載入和使用別的模組,傳入一個模組名,返回一個模組匯出對象。require方法接受以下幾種參數的傳遞:

  • http、fs、path等。原生模組。
  • ./mod或../mod。相對路徑的檔案模組。
  • /a/mod,絕對路徑的檔案模組。
  • mod,非原生模組的檔案模組。
exports

exports對象是當前模組的匯出對象,用於匯出模組公有方法和屬性。別的模組通過require函數使用當前模組時得到的就是當前模組的exports對象。

module

通過module對象可以訪問到當前模組的一些相關資訊,但最多的用途是替換當前模組的匯出對象

  • module.exports :{Object}類型,模組系統自動產生。

  • module.require(id)

id {String} 
Return: {Object} 已解析模組的 module.exports 
這個方法提供了一種像 require() 一樣從最初的模組載入一個模組的方法。

    • module.id:{String}類型,用於區別模組的標識符。通常是完全解析後的檔案名稱

    • module.filename:{String}類型,模組完全解析後的檔案名稱。

    • module.loaded:{Boolean}類型,判斷該模組是否載入完畢。

    • module.parent:{Module Object}類型,返回引入了本模組的其他模組。

    • module.children:{Array}類型,該模組所引入的其他子模組。

demo1 module.exports的使用

sayHello.js:

function sayHello() {    console.log(‘hello‘);}module.exports = sayHello;



app.js:

var sayHello = require(‘./sayHello‘);sayHello();//hello

代碼講解:

定義一個sayHello模組,模組裡定義了一個sayHello方法,通過替換當前模組exports對象的方式將sayHello方法匯出。

在app.js中載入這個模組,得到的是一個函數,調用該函數,控制台列印hello。

 

demo2 匿名替換

sayWorld.js

module.exports = function () {    console.log(‘world‘);}

app.js

var sayWorld = require(‘./sayWorld‘);sayWorld();//world

代碼講解

與上面稍有不同,這次是匿名替換。

demo3 替換為字串

不僅可以替換為方法,也可以替換為字串等。

stringMsg.js

module.exports = ‘i am a string msg!‘;

app.js

var string = require(‘./stringMsg‘);console.log(string);//i am a string msg!

demo4 exports匯出多個變數

當要匯出多個變數怎麼辦呢?這個時候替換當前模組對象的方法就不實用了,我們需要用到exports對象。

useExports.js

exports.a = function () {    console.log(‘a exports‘);}exports.b = function () {    console.log(‘b exports‘);}

 

app,js

var useExports = require(‘./useExports‘);useExports.a();useExports.b();//a exports//b exports
當然,將useExports.js改成這樣也是可以的:
module.exports.a = function () {    console.log(‘a exports‘);}module.exports.b = function () {    console.log(‘b exports‘);}

 

下面通過gif圖進行示範:

module.exports和exports在文章的最後會進行詳細講解。

 

模組初始化

一個模組中的JS代碼僅在模組第一次被使用時執行一次,並在執行過程中初始化模組的匯出對象。之後,緩衝起來的匯出對象被重複利用。

舉個例子,count,js:

var i = 0;function count() {    return ++i;}exports.count = count;

  

app.js

var c1 = require(‘./count‘);var c2 = require(‘./count‘);console.log(c1.count());console.log(c2.count());console.log(co2.count());//1//2//3

可以看到,count.js並沒有因為被require了兩次而初始化兩次。

主模組

通過命令列參數傳遞給NodeJS以啟動程式的模組被稱為主模組。主模組負責調度組成整個程式的其它模組完成工作。例如通過以下命令啟動程式時,我們剛剛一直使用的app.js就是主模組。

二進位模組

雖然一般我們使用JS編寫模組,但NodeJS也支援使用C/C++編寫二進位模組。編譯好的二進位模組除了副檔名是.node外,和JS模組的使用方式相同。雖然二進位模組能使用作業系統提供的所有功能,擁有無限的潛能,但對於不熟悉C/C++的人而言編寫過於困難,並且難以跨平台使用,因此本文不作講解。

模組的載入優先順序

由於Node.js中存在4類別模組(原生模組和3種檔案模組),儘管require方法極其簡單,但是內部的載入卻是十分複雜的,其載入優先順序也各自不同,下面是require載入的邏輯圖:

原生模組在Node.js原始碼編譯的時候編譯進了二進位執行檔案,載入的速度最快。另一類檔案模組是動態載入的,載入速度比原生模組慢。但是Node.js對原生模組和檔案模組都進行了緩衝,於是在第二次require時,是不會有重複開銷的。

exports與module.exports

這裡可能是最容易混淆的地方了。

我們先來看一個例子:

modOne.js

exports.hello = function () {    console.log("hello");}module.exports = function () {    console.log(‘world‘);}

app.js

var one = require(‘./modOne‘);//one.hello(); //執行這句話會報錯one.hello is not a functionone() //列印world

這是為什麼呢?我們得先從exports 與module.exports 說起。

其實,exports 是module.exports的一個引用,exports 的地址指向module.exports。

而我們的modOne.js中通過module.exports = function的方式將module.exports給替換掉了。

而require方法所返回的是module.exports這個實實在在的對象,但是它已經被替換成了function,這就導致了exports指向了空,所以,你所定義的exports.hello是無效的。

用一個通俗易懂的例子來重新解釋一遍。

比如你在電腦的D盤下建立了一個exports文字文件,然後你右鍵->發送到案頭捷徑。

D盤就相當於nodejs中的module,這個exports文字文件就相當於nodejs中模組的exports對象,捷徑就相當於nodejs中指向exports對象引用

D:/exportes.txt ==> module.exportes 
exportes.txt捷徑 ==> exportes

然後,你看exportes.txt不爽,把它給刪了,然後建立了一個word文檔–exports.docx。

這個時候你案頭上的捷徑就沒用了,雖然也叫exports,但是你是訪問不到這個新的word檔案的。

對於nodejs也一樣,當你把module.exportes對象覆蓋了,換成了其他東西的時候,exportes這個引用就失效了。

同樣,我們還可以用這個例子來理解為什麼exportes也可以用來匯出模組。

我們是這樣使用exportes的:

exports.hello = function () {    console.log("hello");}

這段代碼其實等同於:

module.exports.hello = function () {    console.log("hello");}

怎麼理解呢。還是剛才的txt檔案,這次沒有刪除。

D:/exportes.txt ==> module.exportes 
exportes.txt捷徑 ==> exportes

你在案頭開啟了exportes.txt捷徑,然後在裡面輸入hello,然後儲存,關閉。

你再開啟D:/exportes.txt,你會發現你可以看到剛剛寫的hello,你又在後面添加了一句“world”,儲存關閉。

返回案頭,開啟捷徑,你會看到helloworld。

所以說你使用’exports.屬性’和’module.exportes.屬性’是等同的。

這也就能很好的解釋下面這個問題了:

exports = function() {   console.log(‘hello‘);}//這樣寫會報錯

這樣相當於把exprots這個引用覆蓋掉了,你把txt檔案的捷徑改成docx的捷徑還能開啟原來的txt檔案嗎?顯然是不能的。

最後做一個總結:

當我們想讓模組匯出的是一個對象時, 使用exports 和 module.exports 都可以(但 exports 也不能重新覆蓋為一個新的對象),而當我們想匯出非對象介面時,就必須也只能覆蓋 module.exports 。

 

nodejs的模組系統(執行個體分析exprots和module.exprots)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.