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

Xia*_*com 253 javascript commonjs node.js

在此页面(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")
}
Run Code Online (Sandbox Code Playgroud)

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

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

got*_*top 575

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;
Run Code Online (Sandbox Code Playgroud)

如果你设置一个属性exports,就像exports.a = 9;设置一样,module.exports.a因为对象在JavaScript中作为引用传递,这意味着如果你将多个变量设置为同一个对象,它们都是同一个对象; 所以然后exportsmodule.exports是相同的对象.
但是如果你设置exports为新的东西,它将不再被设置为module.exports,因此exports并且module.exports不再是同一个对象.

  • 为什么!?为什么人们只能在这里读到这个.这应该是每个模块化javaScript的标语.谢谢 (12认同)
  • 对,它只是参考类型的基础. (9认同)
  • 精美解释! (5认同)
  • 很好的解释。`module.exports`的文档也对此进行了描述:https://nodejs.org/api/modules.html#modules_module_exports (3认同)
  • 很棒,最好的答案!! (2认同)
  • 这是导出文档:https://nodejs.org/api/modules.html#modules_exports_shortcut,这给了我另一种理解的观点 (2认同)

小智 42

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

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

greet.js

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

module.exports = greet;
Run Code Online (Sandbox Code Playgroud)

上面的代码在nodejs源代码中包装为IIFE(Immediately Invoked Function 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
Run Code Online (Sandbox Code Playgroud)

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

现在,假设您重写greet.js为

exports = function () {
   console.log('Hello World');
};
console.log(exports);
console.log(module.exports);
Run Code Online (Sandbox Code Playgroud)

输出将是

[Function]
{}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

console.log(exports);
console.log(module.exports);
Run Code Online (Sandbox Code Playgroud)

输出将是

{ a: [Function] }
{ a: [Function] }
Run Code Online (Sandbox Code Playgroud)

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

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


Rod*_*nas 20

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

math.js

this.add = function (a, b) {
    return a + b;
};
Run Code Online (Sandbox Code Playgroud)

client.js

var math = require('./math');
console.log(math.add(2,2); // 4;
Run Code Online (Sandbox Code Playgroud)

很棒,在这种情况下:

console.log(this === module.exports); // true
console.log(this === exports); // true
console.log(module.exports === exports); // true
Run Code Online (Sandbox Code Playgroud)

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

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

math.js

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

module.exports = {
    add: add
};
Run Code Online (Sandbox Code Playgroud)

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

console.log(this === module.exports); // false
console.log(this === exports); // true
console.log(module.exports === exports); // false
Run Code Online (Sandbox Code Playgroud)

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

另一种方法是:

math.js

module.exports.add = function (a, b) {
    return a + b;
};
Run Code Online (Sandbox Code Playgroud)

要么:

math.js

exports.add = function (a, b) {
    return a + b;
};
Run Code Online (Sandbox Code Playgroud)


fen*_*huo 14

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

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

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

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

  • @GuyFreakz我不确定这是否说明你的困惑,但`module.exports`和`exports`只是单独的变量,初始化为引用同一个对象.如果更改一个变量引用的内容,则这两个变量不再引用相同的内容.上面的代码行确保将两个变量初始化为同一个新对象. (5认同)

小智 5

节点做了这样的事情:

\n
module.exports = exports = {}\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

\n
module.exports.PI = 3.14\n
Run Code Online (Sandbox Code Playgroud)\n

我们可以写

\n
exports.PI = 3.14\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
exports.add = function(){\n.\n.\n} \n
Run Code Online (Sandbox Code Playgroud)\n

\xe2\x86\x91 这是可以的,与 module.exports.add = function(){...} 相同

\n
exports = function(){\n.\n.\n} \n
Run Code Online (Sandbox Code Playgroud)\n

\xe2\x86\x91 这是不行的,并且将返回空对象,因为 module.exports 仍然引用 {} 并且导出引用不同的对象。

\n