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

在此页面上(http://docs.nodejitsu.com/articles/getting-started/what-is-require),它声明"If you want to set the exports object to a function or a new object, you have to use the module.exports object."

我的问题是为什么 .

// 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 . 它很有用,但没有解释为什么以这种方式设计它的原因 . 如果直接退回出口参考会有问题吗?

回答(4)

3 years ago

module 是一个具有 exports 属性的纯JavaScript对象 . exports 是一个普通的JavaScript变量,恰好设置为 module.exports . 在文件的末尾,node.js基本上会'return' 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 不再是同一个对象 .

3 years ago

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

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

greet.js

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

module.exports = greet;

上面的代码在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

并调用上述函数(.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是空的 .

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

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

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来避免混淆 . 希望这可以帮助 . 快乐编码:)

3 years ago

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

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,因为创建了一个新对象 .

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

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

另一种方法是:

math.js

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

要么:

math.js

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

3 years ago

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

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

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

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