首页 文章

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

提问于
浏览
203

在此页面上(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 回答

  • 30

    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 不再是同一个对象 .

  • 13

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

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

  • 16

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

    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;
    };
    
  • 471

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

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

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

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

相关问题