CommonJs模块系统中“module.exports”和“exports”的区别

在这个页面上( http://docs.nodejitsu.com/articles/getting-started/what-is-require ),它指出:“如果你想把exports对象设置为一个函数或一个新的对象,你必须使用module.exports对象“。

我的问题是为什么。

// right module.exports = function () { console.log("hello world") } // wrong exports = function () { console.log("hello world") } 

我console.logged结果( result=require(example.js) ),第一个是[Function]第二个是{}

你能解释一下它背后的原因吗? 我在这里阅读这篇文章: module.exports vs在Node.js中导出 。 这是有帮助的,但没有解释为什么这样devise的原因。 如果直接返回出口的参考资料,会出现问题吗?

module是一个带有exports属性的普通JavaScript对象。 exports是一个普通的JavaScriptvariables,恰好被设置为module.exports 。 在文件的最后,node.js基本上将module.exports返回给require函数。 在Node中查看JS文件的简单方法可能是这样的:

 var module = { exports: {} }; var exports = module.exports; // your code return module.exports; 

如果你设置一个exports属性,比如exports.a = 9; ,也会设置module.exports.a ,因为对象是作为JavaScript中的引用传递的,这意味着如果你为同一个对象设置多个variables,它们都是同一个对象; 那么exportsmodule.exports是同一个对象。
但是,如果将exports设置为新的内容,则不再将其设置为module.exports ,因此exportsmodule.exports不再是同一个对象。

蕾妮的回答很好解释。 除了用一个例子来回答:

节点为你的文件做了很多事情,其中​​一个重要的事情是包装你的文件。 在nodejs内部返回源代码“module.exports”。 让我们退后一步,了解包装。 假设你有

greet.js

 var greet = function () { console.log('Hello World'); }; module.exports = greet; 

上面的代码被封装为nodejs源代码中的IIFE(即时调用函数expression式),如下所示:

 (function (exports, require, module, __filename, __dirname) { //add by node var greet = function () { console.log('Hello World'); }; module.exports = greet; }).apply(); //add by node return module.exports; //add by node 

并调用上述函数(.apply())并返回module.exports。 此时module.exports和exports指向相同的引用。

现在,想象你重写greet.js为

 exports = function () { console.log('Hello World'); }; console.log(exports); console.log(module.exports); 

输出将是

 [Function] {} 

原因是:module.exports是一个空的对象。 我们没有设置任何东西到module.exports而是我们设置exports = function()…..在新的greet.js。 所以,module.exports是空的。

技术上出口和module.exports应该指向相同的参考(即正确!!)。 但是,在将function()分配给导出时,我们使用“=”,这会在内存中创build另一个对象。 所以,module.exports和exports会产生不同的结果。 说到出口,我们不能重写它。

现在,想象你重写(这称为突变)greet.js(指Renee答案)为

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

输出将是

 { a: [Function] } { a: [Function] } 

正如你所看到的module.exports和exports是指向相同的引用,这是一个函数。 如果你在导出上设置属性,那么它将被设置在module.exports上,因为在JS中,对象是通过引用传递的。

结论总是使用module.exports来避免混淆。 希望这可以帮助。 快乐编码:)

另外,有一件事可能有助于理解:

math.js

 this.add = function (a, b) { return a + b; }; 

client.js

 var math = require('./math'); console.log(math.add(2,2); // 4; 

伟大的,在这种情况下:

 console.log(this === module.exports); // true console.log(this === exports); // true console.log(module.exports === exports); // true 

因此,默认情况下,“this”实际上等于module.exports。

但是,如果您将您的实施更改为:

math.js

 var add = function (a, b) { return a + b; }; module.exports = { add: add }; 

在这种情况下,它会正常工作,但是,“this”不等于module.exports,因为创build了一个新的对象。

 console.log(this === module.exports); // false console.log(this === exports); // true console.log(module.exports === exports); // false 

而现在,require的返回值是module.exports中定义的内容,而不是this或者exports。

另一种做法是:

math.js

 module.exports.add = function (a, b) { return a + b; }; 

要么:

math.js

 exports.add = function (a, b) { return a + b; }; 

Rene关于exportsmodule.exports之间的关系的回答是非常明确的,这些都是关于javascript引用的。 只是想补充一点:

我们在许多节点模块中看到这一点:

var app = exports = module.exports = {};

这将确保即使我们改变了module.exports,我们仍然可以通过使这两个variables指向同一个对象来使用导出。