Node.js module.exports 的目的是什么,你如何使用它?

IT技术 javascript node.js
2021-01-29 01:38:24

Node.js module.exports 的目的是什么,你如何使用它?

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

根据Node.js 文档

module

对当前 module. 特别module.exports 是与导出对象相同。请参阅 src/node.js以获取更多信息。

但这并没有真正的帮助。

究竟module.exports做了什么,一个简单的例子是什么?

6个回答

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

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

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

导出(或“公开”)内部作用域的函数myFunc1myFunc2.

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

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

最后一行显示了结果如何require(通常)只是一个可以访问其属性的普通对象。

注意:如果您覆盖,exports那么它将不再引用module.exports. 因此,如果您希望分配一个新对象(或函数引用),exports那么您还应该将该新对象分配给module.exports


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

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

其次是:

const m = require('./mymodule');
m.shortName(); // invokes module.myVeryLongInternalName
很好的答案 - 在我看来,“exposes”比“exports”是更好的术语选择
2021-03-12 01:38:24
一定要使用:require('./module_name') 语法,因为可能还有一些其他的 node.js module具有一些名称,而不是选择你自己的module,它会选择与 node.js 一起安装的module
2021-04-03 01:38:24
module require 应该是var m = require('./mymodule'); , 点和斜线。这样 Node.js 就知道我们正在使用本地module。
2021-04-04 01:38:24
@UpTheCreek 长期以来一直将module公开的公共符号称为“导出”,这可以追溯到许多编程系统和几十年。这不是 Node 开发人员发明的新术语。
2021-04-04 01:38:24
@ApopheniaOverload - 您可以执行“exports.func1、exports.func2 等”以从一个文件中获得多个公开的方法。
2021-04-07 01:38:24

这已经得到了回答,但我想补充一些说明......

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

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

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

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

(计数器.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

简单来说,您可以将所需文件视为返回单个对象的函数,并且您可以通过将属性(字符串、数字、数组、函数、任何东西)设置为 来向返回的对象添加属性exports

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

(sayhello.js):

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

(app.js):

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

export 和 module.exports 之间的区别在这个答案中得到了更好的解释

module.exports = exports = function(){...}第二个exports只是一个变量对吗?换句话说,它可以是module.exports = abc = function()
2021-03-11 01:38:24
发现增量示例非常好,每当我对导出所做的事情感到超载时,我都会使用它来刷新我的想法。
2021-03-17 01:38:24
我不得不调整你的 module.exports 示例以使其工作。文件:var sayHello = require('./ex6_module.js'); console.log(sayHello());和module:module.exports = exports = function() { return "Hello World!"; }
2021-03-26 01:38:24
@user301639 您可以使用相对路径来遍历文件系统层次结构。require相对于您执行的文件夹开始node app.js。我建议您发布一个带有显式代码 + 文件夹结构示例的新问题,以获得更清晰的答案。
2021-03-28 01:38:24
我如何调用其他文件夹中的某个module,而该文件夹没有我的某个根文件夹?
2021-04-03 01:38:24

请注意,NodeJS module机制基于CommonJSmodule,这些module在许多其他实现中得到支持,例如RequireJS,但也支持SproutCoreCouchDBWakandaOrientDBArangoDBRingoJSTeaJSSilkJScurl.js甚至Adobe Photoshop(通过PSLib)。您可以在此处找到已知实现的完整列表

除非您的module使用特定于节点的功能或module,否则我强烈建议您使用它exports代替module.exports CommonJS 标准的一部分,然后其他实现大多不支持。

另一个 NodeJS 特定功能是当您分配对新对象的引用时,exports而不是像 Jed Watson 在此线程中提供的最后一个示例中那样仅向其添加属性和方法。我个人不鼓励这种做法,因为这破坏了 CommonJS module机制的循环引用支持并非所有实现都支持它,然后 Jed 示例应该以这种方式(或类似的方式)编写以提供更通用的module:

(sayhello.js):

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

(app.js):

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

或者使用 ES6 特性

(sayhello.js):

Object.assign(exports, {
    // Put all your public API here
    sayhello() {
        console.log("Hello World!");
    }
});

(app.js):

const { sayHello } = require('./sayhello');
sayHello(); // "Hello World!"

PS:看起来Appcelerator也实现了CommonJSmodule,但是没有循环引用支持(参见:Appcelerator和CommonJSmodule(缓存和循环引用)

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

1. 以前附加到原始对象exportsmodule.exports当然丢失的所有属性/方法,因为导出的对象现在将引用另一个新对象

这是显而易见的,但是如果您在现有module的开头添加导出的方法,请确保本机导出的对象最后没有引用另一个对象

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. 如果其中之一exportsmodule.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. 棘手的后果。如果更改,参照上述两个exportsmodule.exports,很难说哪个API暴露(它看起来像module.exports胜)

// 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() {}; 
实际上运行前两个示例会产生与声称的结果不同的结果。一旦module完成运行,无论 module.exports 设置为什么,都将始终是导出的内容。export 变量只是 module.exports 的局部变量别名(就好像let exports = module.exports每个module的第一行一样)。永远不应重新分配此变量 - 这样做只会导致您丢失该别名,并且不会更改导出的内容。exports = 'abc'不会导出'abc'。
2021-03-28 01:38:24

module.exports 属性或exports 对象允许module选择应该与应用程序共享的内容

在此处输入图片说明

我有一个关于 module_export 的视频在这里可用