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

时间:2013-05-05 10:59:27

标签: javascript node.js commonjs

在此页面(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")
}

我控制台。记录了结果(result=require(example.js)),第一个是[Function],第二个是{}

请你解释背后的原因吗?我在这里阅读帖子:module.exports vs exports in Node.js。它很有帮助,但没有解释它以这种方式设计的原因。如果直接退回出口参考会有问题吗?

8 个答案:

答案 0 :(得分:530)

module是一个具有exports属性的纯JavaScript对象。 exports是一个普通的JavaScript变量,恰好设置为module.exports。 在文件的末尾,node.js将基本上“返回”module.exportsrequire函数。在Node中查看JS文件的简单方法是:

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

// your code

return module.exports;

如果您在exports上设置属性,例如exports.a = 9;,则会设置module.exports.a,因为对象在JavaScript中作为引用传递,这意味着如果您设置多个变量对于同一个对象,它们所有相同的对象;因此,exportsmodule.exports是同一个对象 但是,如果您将exports设置为新的,则不再将其设置为module.exports,因此exportsmodule.exports不再是同一个对象。

答案 1 :(得分:37)

Renee的答案得到了很好的解释。除了一个例子的答案:

Node为您的文件做了很多事情,其中​​一个重要的是WRAPPING您的文件。内部nodejs源代码" module.exports"退回。让我们退后一步,了解包装器。假设你有

greet.js

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

module.exports = greet;

以上代码在nodejs源代码中包装为IIFE(立即调用函数表达式),如下所示:

(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指向同一个引用。

现在,想象一下你重写

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

输出将是

[Function]
{}

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

技术上,exports和module.exports应指向相同的引用(这是正确的!!)。但我们使用" ="将函数()....赋值给exports时,会在内存中创建另一个对象。因此,module.exports和exports会产生不同的结果。谈到出口,我们无法覆盖它。

现在,想象一下你重写(这叫做Mutation) greet.js(指Renee回答)为

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

console.log(exports);
console.log(module.exports);

输出将是

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

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

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

答案 2 :(得分:19)

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

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

因此,默认情况下,"这个"实际上等于module.exports。

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

math.js

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

module.exports = {
    add: add
};

在这种情况下,它会正常工作,然而,#34;这个"不再等于module.exports,因为创建了一个新对象。

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

现在,require将返回的内容是module.exports中定义的内容,而不是此内容或导出内容。

另一种方法是:

math.js

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

或者:

math.js

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

答案 3 :(得分:13)

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

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

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

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

答案 4 :(得分:0)

myTest.js

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

exports.put = function () {};

console.log(module.exports)
// output: { get: [Function], put: [Function] }

exportsmodule.exports是相同的,并且是对同一对象的引用。您可以根据需要通过两种方式添加属性。

答案 5 :(得分:0)

由于上面发布的所有答案都得到了很好的解释,我想添加一些我今天面临的问题。

当您使用导出导出某些内容时,您必须将其与变量一起使用。喜欢,

File1.js

exports.a = 5;

在另一个文件中

File2.js

const A = require("./File1.js");
console.log(A.a);

并使用module.exports

File1.js

module.exports.a = 5;

在 File2.js 中

const A = require("./File1.js");
console.log(A.a);

默认 module.exports

File1.js

module.exports = 5;

在 File2.js 中

const A = require("./File2.js");
console.log(A);

答案 6 :(得分:0)

node 做了这样的事情:

module.exports = exports = {}

module.exports 和exports 引用同一个对象。

这样做只是为了方便。 所以不要写这样的东西

module.exports.PI = 3.14

我们可以写

exports.PI = 3.14

因此可以将属性添加到导出,但将其分配给不同的对象则不行

exports.add = function(){
.
.
} 

↑ 这个没问题,和 module.exports.add = function(){...} 一样

exports = function(){
.
.
} 

↑ 这不行,并且会返回空对象作为模块。exports 仍然引用 {},exports 引用不同的对象。

答案 7 :(得分:0)

module.exportsexports 之间有两个区别

  1. 当将单个类、变量或函数从一个模块导出到另一个模块时,我们使用 module.exports。但是导出到多个变量或函数从一个模块到另一个模块,我们使用exports

  2. module.exports 是从 require() 调用返回的对象引用。但是 exports 不是由 require() 返回的。

通过示例查看更多详细信息 >> link