2014-01-31 58 views
5

我一直在閱讀關於這個話題幾個小時,只是沒有找到任何東西來幫助製作這個棒。什麼是module.exports vs exports之間的模塊和區別?

模塊只是具有少數屬性的節點中的對象,一個是引用對象的exports屬性。

的「出口」變量是

var exports = module.exports 

這是一個變種指向的是module.exports被引用的對象。

我在努力的是看到模塊是什麼。我知道這是一個對象,但是隻有一個?

我知道這是不準確的方式節點實現一個模塊,但我形象化它看起來像這樣:現在

var module = {} 

module.exports = {} 

// now module has a property module.exports 

var exports = module.exports 

,從一切,我一直在讀,如果你指定的東西module.export ='xyz'

它將保存值'xyz'。它會丟失原始對象嗎?最重要的是,如果我在同一個文件中爲module.exports指定了其他內容,它會被替換爲新值嗎?

EX: 

// file = app.js 

module.export = 'hello' 
module.export = 'bye' 

// file = newApp.js 

require(./app); 

該模塊的價值是什麼?我重寫相同的模塊對象還是有多個?

回答

7

在我們繼續之前,重要的是您要了解how modules are actually loaded by node

採取從節點的模塊加載系統遠離關鍵的一點是,它實際上運行的代碼之前,你require(這發生在Module#_compile),它creates一個新的空exports對象爲Module的屬性。 (換句話說,你的可視化是正確的。)

節點然後wraps文本在require d文件與匿名函數:

(function (exports, require, module, __filename, __dirname) { 
// here goes what's in your js file 
}); 

...,基本上eval s表示字符串。的eval荷蘭國際集團該字符串的結果是一個函數(匿名包裝之一),和節點立即調用的函數,傳遞中的參數是這樣的:

evaledFn(module.exports, require, module, filename, dirname); 

requirefilename,和dirname是到參考require函數(其實際isn't a global)和包含加載模塊的路徑信息的字符串。 (這是當他們說什麼docs意味着「__filename並不是一個真正的全球性,而是本地的每一個模塊。」)

因此,我們可以看到,一個模塊內,確實exports === module.exports。但爲什麼模塊同時得到moduleexports

向後兼容性。在節點的最初幾天,模塊內部沒有任何module變量。您只需分配到exports。但是,這意味着你永遠不能將構造函數作爲模塊本身導出。

模塊導出一個構造函數作爲模塊的熟悉的例子:

var express = require('express'); 
var app = express(); 

這工作,因爲Express exports a function by reassigning module.exports,覆蓋空對象節點在默認情況下爲您提供:

module.exports = function() { ... } 

然而,請注意,您不能只寫:

exports = function { ... } 

T他是因爲JavaScript's parameter passing semantics are weird。從技術上講,JavaScript可能被認爲是「純粹按價值傳遞」,但實際上參數通過「按參考值」傳遞。

這意味着,當您將對象傳遞給函數時,它會將該對象的引用作爲值接收。您可以通過該引用訪問原始對象,但不能更改對引用的調用者的感知。換句話說,沒有像你在C/C++/C#中看到的「out」參數那樣的東西。

舉一個具體的例子:

var obj = { x: 1 }; 

function A(o) { 
    o.x = 2; 
} 
function B(o) { 
    o = { x: 2 }; 
} 

調用A(obj);將導致obj.x == 2,因爲我們通過訪問作爲o原始對象。但是,B(obj);將無所作爲; obj.x == 1。向B的本地o分配一個全新對象只會改變o指向的內容。它對呼叫者的對象obj沒有任何影響,它不受影響。

現在應該很明顯,爲什麼有必要將module對象添加到節點模塊的本地範圍。爲了允許模塊完全替換exports對象,它必須作爲對象傳遞給模塊匿名函數的屬性使用。顯然沒有人想破壞現有的代碼,因此exports留作module.exports的參考。

因此,當您只是將屬性分配給導出對象時,無論您使用的是exports還是module.exports;它們是同一個,指向完全相同的對象。

當你想給一個函數出口爲一體的頂級出口,你必須使用module.exports,因爲正如我們看到的,只是分配功能exports將有模塊範圍之外沒有影響它的唯一。

最後要注意,當您正在導出一個功能模塊,這是一個很好的做法,分配給exportsmodule.exports。這樣,兩個變量保持一致並且與標準模塊中的變量一樣。

exports = module.exports = function() { ... } 

一定要做到這一點靠近你的模塊文件的頂部,所以沒有人意外分配到exports這最終得到覆蓋。

而且,如果看起來怪你( = S'),我走的事實,即包含assignment operator表達式返回分配的值,這使得它可以在一臺值賦給優勢一次拍攝多個變量。

+0

爲什麼'express'同時分配兩個變量的唯一原因實際上只是「良好實踐」或「以防萬一」?是否真的沒有其他原因,甚至沒有更好或甚至需要的附帶條件? – trysis

1

您正在覆蓋模塊 - 導出是單個對象被require拉入。通常,在使用require進行這種模塊化JavaScript時,導出將是一個構造函數,而不是您的示例中的一個字符串。這樣,您可以創建在模塊中定義的新功能實例。例如:

// module.js 
var MyConstructor = function(prop) { 
    this.prop = prop; 
}); 

module.exports = MyConstructor; 

// app.js 
var MyConstructor = require('module'); 
var instance = new MyConstructor('test'); 
console.log(instance.prop) // 'test' 
2

Node.js中的模塊只是文件。每個文件都是一個模塊,每個模塊都是一個文件。如果你有多個文件,你可以有多個模塊(每個文件一個)。

作爲每module.exportsthe Node.js API documentation將闡明的主題的一些光:

module.exports對象由模塊系統創建的。有時候 這是不可接受的;許多人希望他們的模塊成爲某個類的實例。爲此,將所需的導出對象分配到 module.exports。請注意,將所需的對象分配給exports將 簡單地重新綁定當地exports變量,這可能不是您想要做的 。

exports變量可用內的模塊開始爲 參照module.exports。與任何變量一樣,如果您爲其指定一個新的 值,它將不再綁定到以前的值。 ...作爲指導, 如果出口和模塊之間的關係。出口 對你來說看起來很神奇,忽略出口並且只使用module.exports。

那麼,這一切意味着什麼?在您的特定示例中,module.exports將等於它的上次分配值('bye')。

相關問題