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

IT技术 javascript node.js commonjs
2021-02-05 18:08:48

在此页面上(http://docs.nodejitsu.com/articles/getting-started/what-is-require),它指出“如果要将导出对象设置为函数或新对象,则必须使用 module.exports 对象。”

我的问题是为什么。

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

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

你能解释一下背后的原因吗?我在这里阅读了这篇文章:module.exports 与 Node.js 中的导出这很有帮助,但没有解释为什么这样设计的原因。直接返回exports的reference会不会有问题?

6个回答

module是一个带有exports属性的普通 JavaScript 对象exports是一个普通的 JavaScript 变量,恰好被设置为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 中作为引用传递,这意味着如果你为同一个对象设置多个变量,它们都是同一个对象;所以 thenexportsmodule.exports是同一个对象。
但是如果您设置exports为新的东西,它将不再设置为module.exports,因此exportsmodule.exports不再是同一个对象。

谢谢,但为什么我们需要exportsmodule.exports为了简单起见(和 KISS 原则),我们不能只接受(即)后者吗?
2021-03-13 18:08:48
这里是导出文档:nodejs.org/api/modules.html#modules_exports_shortcut,这让我有了另一种理解
2021-03-23 18:08:48
是的,这只是引用类型的基础知识。
2021-03-27 18:08:48
绝对不需要exports,它只是一个方便的速记。但是现在它永远不能被删除,因为它会破坏所有现有的代码。
2021-03-30 18:08:48
很好的解释。的文档也module.exports描述了它:nodejs.org/api/modules.html#modules_module_exports
2021-04-01 18:08:48

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

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

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

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

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

输出将是

[Function]
{}

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

从技术上讲,导出和 module.exports 应该指向相同的引用(这是正确的!!)。但是我们在将 function().... 分配给导出时使用“=”,这会在内存中创建另一个对象。因此,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 指向相同的引用,这是一个函数。如果您在导出上设置属性,那么它将在 module.exports 上设置,因为在 JS 中,对象是通过引用传递的。

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

这也是一个美丽的有见地的答案,并补充了@goto-bus-stop 的答案。:)
2021-04-03 18:08:48

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

数学.js

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

客户端.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。

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

数学.js

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

module.exports = {
    add: add
};

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

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

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

另一种方法是:

数学.js

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

或者:

数学.js

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

刘若英的约之间的关系答案exportsmodule.exports是相当清楚的,它是所有关于JavaScript的引用。只想补充一点:

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

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

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

其他人在@fengshuo 上错过的实际用例。谢谢!
2021-03-13 18:08:48
我对这个解释感到困惑,请详细说明一下?
2021-03-29 18:08:48
@GuyFreakz我不知道这是否说你的困惑,但module.exportsexports都只是不同的变量,初始化为引用同一个对象。如果更改一个变量引用的内容,则这两个变量不再引用同一事物。上面的代码行确保两个变量都初始化为同一个新对象。
2021-04-06 18:08:48

节点做这样的事情:

module.exports = exports = {}

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

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

module.exports.PI = 3.14

我们可以写

exports.PI = 3.14

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

exports.add = function(){
.
.
} 

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

exports = function(){
.
.
} 

↑ 这不行,并且会返回空对象,因为 module.exports 仍然指的是 {},而exports 指的是不同的对象。