module.exports"和“;exports"在CommonJs模块系统中

Difference between "module.exports" and "exports" in the CommonJs Module System

本文关键字:quot exports 模块 系统 CommonJs module      更新时间:2023-09-26

在这个页面(http://docs.nodejitsu.com/articles/getting-started/what-is-require)上,它声明"如果您想将exports对象设置为一个函数或一个新对象,则必须使用该模块。出口对象!"

我的问题是为什么。

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

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

你能解释一下背后的原因吗?我在这里读了这篇文章:模块。Node.js中的exports和exports。这是有帮助的,但并没有解释为什么它是这样设计的。如果直接退回出口的引用会有问题吗?

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中是作为引用传递的,这意味着如果你为同一个对象设置了多个变量,它们都是相同的对象;所以exportsmodule.exports是同一个对象。
但是如果您将exports设置为新的对象,它将不再设置为module.exports,因此exportsmodule.exports不再是同一个对象。

蕾妮的回答解释得很好。在答案后面加上一个例子:

Node对你的文件做了很多事情,其中最重要的是包装你的文件。内部nodejs源代码"模块。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。此时模块。导出和导出指向相同的引用。

现在,想象你重写了

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

输出将是

[Function]
{}

原因是:module。Exports是一个空对象。我们没有设置任何模块。我们设置Exports = function().....在新的greeting .js中。因此,模块。出口是空的。

技术上是导出和模块。导出应该指向相同的引用(这是正确的!!)但是我们在赋值function()....时使用"="导出,这将在内存中创建另一个对象。因此,模块。出口和出口产生不同的结果。当涉及到出口时,我们无法覆盖它。

现在,假设您重新编写(这称为突变)greeting .js(指向Renee answer)为

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

输出将是

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

可以看到模块。Exports和Exports都指向同一个引用,它是一个函数。如果你在exports上设置了一个属性,那么它也会在module上设置。因为在JS中,对象是通过引用传递的。

结论总是使用模块。出口避免混淆。希望这对你有所帮助。快乐编码:)

同样,有一件事可能有助于理解:

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

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

现在,require返回的是模块内部定义的内容。导出,不再是这个或导出。

另一种方法是:

math.js

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

或:

math.js

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

Rene关于exportsmodule.exports之间关系的回答非常清楚,这都是关于javascript引用的。我想再加一句:

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

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

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

节点执行如下操作:

module.exports = exports = {}

模块。

这样做只是为了方便。所以与其写这样的

module.exports.PI = 3.14

我们可以写

exports.PI = 3.14

所以给exports添加一个属性是可以的,但是把它赋值给一个不同的对象是不可以的

exports.add = function(){
.
.
} 

↑这是OK的,与module.exports.add = function(){…}相同

exports = function(){
.
.
} 

↑this是不允许的,并且将返回一个空对象作为模块。Exports仍然指向{},Exports指向不同的对象

module.exportsexports有两个区别

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

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

查看示例中的更多细节>>链接

由于上面张贴的所有答案都解释得很好,我想补充一些我今天遇到的问题。

当你使用exports导出一些东西时,你必须使用变量。如,

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);

default module.exports

File1.js

module.exports = 5;
在File2.js

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

myTest.js

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

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

您可以将导出视为模块的快捷方式。内的导出给定的模块。实际上,exports只是一个得到的变量初始化为module的值。导出模块评估。该值是一个对象(空对象)的引用这种情况下)。这意味着导出包含对相同内容的引用由module.exports引用的对象。这也意味着通过赋值导出的另一个值不再绑定到module.exports.

这个来自MDN的解释对我来说是最清楚的。

基本上,内存中有一个对象由两个变量- exports和module.exports引用。

exports.a = 23

=

module.exports = {a:23}

,

exports = {a:23}

不等于

module.exports = {a:23}

当你把一个新的对象直接赋值给exports变量,那么这个变量就不再引用module.exports