Node.js module.exports的用途是什么?您如何使用它?

我似乎找不到关于这个的任何信息,但它似乎是Node.js的一个相当重要的部分,因为我经常在源代码中看到它。

根据Node.js文档:

单元参考电流单元特别是module.exports与导出对象相同。看见src/node.js获取更多信息。

但这并没有真正的帮助。

module.exports到底做什么?一个简单的例子是什么?


当前回答

如果将对新对象的引用分配给exports和/或modules.exports,则必须注意以下几点:

1.以前附加到原始导出或模块的所有财产/方法都会丢失。导出当然会丢失,因为导出的对象现在将引用另一个新的对象

这一点很明显,但如果在现有模块的开头添加导出方法,请确保本机导出对象在末尾没有引用另一个对象

exports.method1 = function () {}; // exposed to the original exported object
exports.method2 = function () {}; // exposed to the original exported object

module.exports.method3 = function () {}; // exposed with method1 & method2

var otherAPI = {
    // some properties and/or methods
}

exports = otherAPI; // replace the original API (works also with module.exports)

2.如果exports或module.exports中的一个引用新值,则它们不再引用同一对象

exports = function AConstructor() {}; // override the original exported object
exports.method2 = function () {}; // exposed to the new exported object

// method added to the original exports object which not exposed any more
module.exports.method3 = function () {}; 

3.狡猾的后果。如果同时更改了对exports和module.exports的引用,很难说哪个API是公开的(看起来module.export获胜)

// override the original exported object
module.exports = function AConstructor() {};

// try to override the original exported object
// but module.exports will be exposed instead
exports = function AnotherConstructor() {}; 

其他回答

如果将对新对象的引用分配给exports和/或modules.exports,则必须注意以下几点:

1.以前附加到原始导出或模块的所有财产/方法都会丢失。导出当然会丢失,因为导出的对象现在将引用另一个新的对象

这一点很明显,但如果在现有模块的开头添加导出方法,请确保本机导出对象在末尾没有引用另一个对象

exports.method1 = function () {}; // exposed to the original exported object
exports.method2 = function () {}; // exposed to the original exported object

module.exports.method3 = function () {}; // exposed with method1 & method2

var otherAPI = {
    // some properties and/or methods
}

exports = otherAPI; // replace the original API (works also with module.exports)

2.如果exports或module.exports中的一个引用新值,则它们不再引用同一对象

exports = function AConstructor() {}; // override the original exported object
exports.method2 = function () {}; // exposed to the new exported object

// method added to the original exports object which not exposed any more
module.exports.method3 = function () {}; 

3.狡猾的后果。如果同时更改了对exports和module.exports的引用,很难说哪个API是公开的(看起来module.export获胜)

// override the original exported object
module.exports = function AConstructor() {};

// try to override the original exported object
// but module.exports will be exposed instead
exports = function AnotherConstructor() {}; 

模块系统的目的是什么?

它完成了以下任务:

使我们的文件从膨胀到真正的大尺寸。在开发过程中,通常很难处理包含5000行代码的文件。强制分离关注点。将代码拆分为多个文件可以让我们为每个文件指定适当的文件名。通过这种方式,我们可以很容易地确定每个模块都做什么以及在哪里找到它(假设我们制作了一个逻辑目录结构,这仍然是您的责任)。

拥有模块可以更容易地找到代码的某些部分,从而使代码更易于维护。

它是如何工作的?

NodejS使用CommomJS模块系统,其工作方式如下:

如果文件想要导出某些内容,则必须使用module.export语法声明它如果文件要导入某个内容,则必须使用require('file')语法声明它

例子:

测试1.js

const test2 = require('./test2');    // returns the module.exports object of a file

test2.Func1(); // logs func1
test2.Func2(); // logs func2

测试2.js

module.exports.Func1 = () => {console.log('func1')};

exports.Func2 = () => {console.log('func2')};

其他需要了解的有用信息:

正在缓存模块。当您在两个不同的文件中加载相同的模块时,该模块只需加载一次。第二次对同一模块调用require()时,将从缓存中取出。模块以同步方式加载。这个行为是必需的,如果它是异步的,我们无法立即访问从require()中检索的对象。

module.exports是作为require调用的结果实际返回的对象。

exports变量最初被设置为同一个对象(即,它是一个简写“别名”),因此在模块代码中,您通常会编写如下内容:

let myFunc1 = function() { ... };
let myFunc2 = function() { ... };
exports.myFunc1 = myFunc1;
exports.myFunc2 = myFunc2;

导出(或“公开”)内部作用域函数myFunc1和myFunc2。

在调用代码中,您将使用:

const m = require('./mymodule');
m.myFunc1();

其中最后一行显示require的结果(通常)只是一个可以访问其财产的普通对象。

注意:如果您覆盖导出,则它将不再引用module.exports。因此,如果您希望为导出分配新对象(或函数引用),则还应将该新对象分配给module.export


值得注意的是,添加到导出对象的名称不必与要添加的值的模块内部作用域名称相同,因此可以:

let myVeryLongInternalName = function() { ... };
exports.shortName = myVeryLongInternalName;
// add other objects, functions, as required

然后:

const m = require('./mymodule');
m.shortName(); // invokes module.myVeryLongInternalName

这已经得到了回答,但我想补充一些澄清。。。

您可以使用exports和module.exports将代码导入应用程序,如下所示:

var mycode=require('./path/to/mycode');

您将看到的基本用例(例如在ExpressJS示例代码中)是在.js文件中的exports对象上设置财产,然后使用require()导入该文件

因此,在一个简单的计数示例中,您可以:

(counter.js):

var count = 1;

exports.increment = function() {
    count++;
};

exports.getCount = function() {
    return count;
};

…然后在您的应用程序(web.js或任何其他.js文件)中:

var counting = require('./counter.js');

console.log(counting.getCount()); // 1
counting.increment();
console.log(counting.getCount()); // 2

简单地说,您可以将所需文件视为返回单个对象的函数,并且可以通过在导出时设置它们,将财产(字符串、数字、数组、函数等)添加到返回的对象中。

有时您会希望require()调用返回的对象是可以调用的函数,而不仅仅是具有财产的对象。在这种情况下,还需要设置module.exports,如下所示:

(sayhello.js):

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

(app.js):

var sayHello = require('./sayhello.js');
sayHello(); // "Hello World!"

这里的答案更好地解释了exports和module.exports之间的区别。

参考链接如下:

exports = module.exports = function(){
    //....
}

导出或模块的财产。导出,例如函数或变量,将在外部公开

有一点你必须更加注意:不要覆盖导出。

为什么?

因为导出只是module.exports的引用,所以可以将财产添加到导出中,但如果覆盖导出,引用链接将断开。

很好的例子:

exports.name = 'william';

exports.getName = function(){
   console.log(this.name);
}

坏例子:

exports = 'william';

exports = function(){
     //...
}

如果您只想公开一个函数或变量,如下所示:

// test.js
var name = 'william';

module.exports = function(){
    console.log(name);
}   

// index.js
var test = require('./test');
test();

这个模块只公开了一个函数,name的属性对外是私有的。