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

What is the purpose of Node.js module.exports and how do you use it?

提问人:mrwooster 提问时间:3/15/2011 最后编辑:lifeisfoomrwooster 更新时间:7/8/2022 访问量:525639

问:

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

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

根据 Node.js 文档

模块

对当前 .特别是与导出对象相同。有关详细信息,请参阅。modulemodule.exportssrc/node.js

但这并没有真正的帮助。

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

JavaScript 节点 .js

评论

0赞 user202729 12/11/2021
另请参阅:javascript - module.exports 与 Node.js 中的导出 - 堆栈溢出

答:

1683赞 Alnitak 3/15/2011 #1

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

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

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

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

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

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

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

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


值得注意的是,添加到对象的名称不必与要添加的值的模块的内部范围名称相同,因此可以具有:exports

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

其次:

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

评论

131赞 UpTheCreek 3/25/2012
好的答案——在我看来,“暴露”比“出口”是更好的术语选择
2赞 hellatan 8/1/2012
@ApopheniaOverload - 您可以执行“exports.func1、exports.func2 等”,从一个文件中公开多个方法。
75赞 Gui Premonsa 10/23/2012
模块 require 应为 var m = require('./mymodule');,带有点和斜杠。这样 Node.js 就知道我们正在使用本地模块。
7赞 Sazid 10/31/2013
请务必使用: require('./module_name') 语法,因为可能还有其他一些 node.js 模块具有某个名称,它不会选择您自己的模块,而是选择与 node 一起安装的模块.js
4赞 Mark Reed 9/4/2014
@UpTheCreek,将模块公开的公共符号称为“导出”的传统由来已久,这可以追溯到许多编程系统和几十年前。这并不是 Node 开发人员发明的新术语。
232赞 Jed Watson 10/16/2012 #2

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

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

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

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

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

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

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

评论

1赞 Jason Lydon 7/23/2014
我必须调整你的module.exports示例才能使其工作。file: 和 module:var sayHello = require('./ex6_module.js'); console.log(sayHello());module.exports = exports = function() { return "Hello World!"; }
1赞 munkee 12/9/2014
发现增量示例非常好,每次我因导出工作而超负荷时,我都用它来刷新我的思绪。
0赞 Jeb50 5/24/2020
module.exports = exports = function(){...}第二个只是一个变量,对吧?换句话说,它可以是exportsmodule.exports = abc = function()
64赞 Alexandre Morgaut 1/20/2013 #3

请注意,NodeJS模块机制基于CommonJS模块,这些模块在许多其他实现中都支持,如RequireJS,但也支持SproutCore,CouchDB,Wakanda,OrientDB,ArangoDB,RingoJS,TeaJS,SilkJS,curl.js,甚至Adobe Photoshop(通过PSLib)。 您可以在此处找到已知实现的完整列表。

除非您的模块使用特定于节点的功能或模块,否则我强烈建议您使用不是 CommonJS 标准的一部分,并且大多数情况下不受其他实现的支持。exportsmodule.exports

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

(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 也实现了 CommonJS 模块,但没有循环引用支持(参见:Appcelerator 和 CommonJS 模块(缓存和循环引用))

35赞 Alexandre Morgaut 1/25/2013 #4

如果将对新对象的引用分配给 和/或 :exportsmodules.exports

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

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

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.棘手的后果。如果将引用更改为 和 ,很难说暴露了哪个 API(看起来获胜)exportsmodule.exportsmodule.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() {}; 

评论

0赞 Scotty Jamison 4/24/2021
实际上,运行前两个示例会产生与声称的结果不同的结果。模块完成运行后,无论 module.exports 设置为什么,都将始终是导出的内容。exports 变量只是 module.exports 的局部变量别名(就好像是每个模块的第一行一样)。永远不应该重新分配这个变量 - 这样做只会导致你失去该别名,并且不会改变导出的内容。即 不会导出“ABC”。let exports = module.exportsexports = 'abc'
31赞 anish 9/4/2014 #5

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

enter image description here

我有一个关于module_export的视频,可以在这里找到

10赞 qianjiahao 4/18/2015 #6

引用链接是这样的:

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

OR的属性,如函数或变量,将被暴露在外部exportsmodule.exports

有一点你必须更注意:不要出口。override

为什么?

因为导出的只是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 的属性对于外部是私有的。

19赞 pspi 8/7/2015 #7

将程序代码划分为多个文件时,用于将变量和函数发布给模块的使用者。源文件中的调用将替换为从模块加载的相应调用。module.exportsrequire()module.exports

编写模块时要记住

  • 模块加载被缓存,只有初始调用评估 JavaScript。
  • 可以在模块中使用局部变量和函数,但并非所有内容都需要导出。
  • 该对象也可用作速记。但是在返回 sole 函数时,请始终使用 .module.exportsexportsmodule.exports

module exports diagram

根据:“模块第 2 部分 - 编写模块”。

5赞 Shantanu Madane 4/23/2016 #8

模块将相关代码封装到单个代码单元中。创建模块时,这可以解释为将所有相关函数移动到文件中。

假设有一个文件Hello.js,其中包含两个函数

sayHelloInEnglish = function() {
  return "Hello";
};
sayHelloInSpanish = function() {
  return "Hola";
};

只有当代码的效用是多个调用时,我们才编写函数。

假设我们想增加函数对其他文件的效用,比如 World.js,在这种情况下,导出一个文件会出现图片,可以通过 module.exports 获得。

您可以通过下面给出的代码导出这两个函数

var anyVariable={
 sayHelloInEnglish = function() {
      return "Hello";
    };
  sayHelloInSpanish = function() {
      return "Hola";
    }; 
}
module.export=anyVariable;

现在,您只需要在 World.js 中要求文件名即可使用这些函数

var world= require("./hello.js");

评论

1赞 divine 4/14/2017
@BenTaliadoros我也认为他迟到了,我也认为他的anyVariable对象有很多错误。上面的行 sayHelloInSpanish 方法不应该以分号 (;) 结尾,并且 sayHelloInSpanish = function 是错误的。这个对象的所有事情都是错误的。我会编辑他的回答
1赞 divine 4/14/2017
编辑被禁用。alphadogg 在这个答案中还编辑了什么??
0赞 Ben Taliadoros 4/25/2017
只是格式化。除非它有一些疯狂的 es6 东西我没有遇到过,而且我确定它不是,否则它根本不是有效的 JS
7赞 user4398985 7/28/2016 #9

当您下载和安装 node.js 时,node.js 中有一些默认或现有模块,如 http、sys 等。

由于它们已经在 node.js 中,当我们想使用这些模块时,我们基本上喜欢导入模块,但为什么呢?因为它们已经存在于 node.js 中。导入就像从 node.js 中获取它们并将它们放入您的程序中。然后使用它们。

导出恰恰相反,您正在创建所需的模块,假设模块添加.js并将该模块放入节点.js中,您可以通过导出它来实现。

在我在这里写任何东西之前,请记住,module.exports.additionTwoexports.additionTwo 相同

呵呵,这就是原因,我们确实喜欢

exports.additionTwo = function(x)
{return x+2;};

小心路径

假设您已经创建了一个 addition.js 模块,

exports.additionTwo = function(x){
return x + 2;
};

在 NODE.JS 命令提示符上运行此命令时:

node
var run = require('addition.js');

这会错误地说

错误:找不到模块添加.js

这是因为 node.js 进程无法添加 .js,因为我们没有提到路径。因此,我们可以使用 NODE_PATH

set NODE_PATH = path/to/your/additon.js

现在,这应该可以成功运行,没有任何错误!

还有一件事,您还可以通过不设置NODE_PATH来运行 addition.js 文件,回到 nodejs 命令提示符:

node
var run = require('./addition.js');

由于我们在这里通过说它在当前目录中来提供路径,因此它也应该成功运行。./

4赞 Moriarty 5/18/2018 #10

其目的是:

模块化编程是一种强调 将程序的功能分离为独立的, 可互换的模块,使得每个模块都包含所有必要的内容 仅执行所需功能的一个方面。

维基百科上的数据

我想如果没有模块化/可重用的代码,编写大型程序会变得很困难。在nodejs中,我们可以创建模块化程序,利用定义我们公开的内容,并用 .module.exportsrequire

试试这个例子:

文件日志.js

function log(string) { require('fs').appendFileSync('log.txt',string); }

module.exports = log;

标准输出日志.js

function log(string) { console.log(string); }

module.exports = log;

程序.js

const log = require('./stdoutLog.js')

log('hello world!');

执行

$ 节点程序.js

世界您好!

现在尝试将 ./stdoutLog.js 换成 ./fileLog.js

3赞 Willem van der Veen 8/26/2018 #11

模块系统的目的是什么?

它完成以下操作:

  1. 防止我们的文件膨胀到非常大的尺寸。在开发过程中,包含 5000 行代码的文件通常很难处理。
  2. 强制分离关注点。将代码拆分为多个文件可以让我们为每个文件使用适当的文件名。这样,我们可以很容易地识别每个模块的作用以及在哪里找到它(假设我们制作了一个逻辑目录结构,这仍然是你的责任)。

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

它是如何工作的?

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

  1. 如果一个文件想要导出一些东西,它必须使用语法来声明它module.export
  2. 如果一个文件想要导入某些内容,它必须使用语法声明它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')};

其他需要了解的有用事项:

  1. 模块正在缓存。当您在 2 个不同的文件中加载相同的模块时,该模块只需加载一次。第二次在同一模块上调用 a 时,将从缓存中提取。require()
  2. 模块是同步加载的。此行为是必需的,如果它是异步的,则我们无法立即访问从中检索到的对象。require()
-7赞 Gtm 8/14/2019 #12
let test = function() {
    return "Hello world"
};
exports.test = test;

评论

5赞 barbsan 8/14/2019
这与已接受答案中的第一个片段中的示例类似(没有区别),但没有任何解释。在回答之前,请确保您的回答会给主题添加一些东西。return "Hello world"
3赞 lifeisfoo 4/10/2022 #13

ECMAScript 模块 - 2022

从 Node 14.0 开始,ECMAScript 模块不再是实验性的,您可以使用它们来代替经典 Node 的 CommonJS 模块。

ECMAScript 模块是打包 JavaScript 代码以供重用的官方标准格式。模块是使用各种 import 和 export 语句定义的。

您可以定义一个导出函数的 ES 模块:

// my-fun.mjs
function myFun(num) {
  // do something
}

export { myFun };

然后,您可以从 my-fun.mjs 导入导出的函数:

// app.mjs
import { myFun } from './my-fun.mjs';

myFun();

.mjs是 Node.js ECMAScript 模块的默认扩展。 但是,您可以在使用 CLI 中的“type”字段或 --input-type 标志将默认模块扩展配置为在解析模块时进行查找。package.json

最新版本的 Node.js 完全支持 ECMAScript 和 CommonJS 模块。此外,它还提供了它们之间的互操作性。

module.exports

ECMAScript 和 CommonJS 模块有很多不同之处,但与这个问题最相关的区别是不再有 s、不再、不再requireexportsmodule.exports

在大多数情况下,ES 模块导入可用于加载 CommonJS 模块。 如果需要,可以使用 module.createRequire() 在 ES 模块中构造一个 require 函数。

ECMAScript 模块发布历史

释放 变化
15.3.0、14.17.0、12.22.0 版 稳定模块实现
14.13.0、12.20.0 版 支持检测 CommonJS 命名导出
14.0.0、13.14.0、12.20.0 版 删除实验模块警告
13.2.0、12.17.0 版 加载 ECMAScript 模块不再需要命令行标志
12.0.0 版 通过“type”字段添加对使用文件扩展名.js ES 模块的支持package.json
8.5.0 版 添加了初始 ES 模块实现

您可以在 Node.js 存储库中找到所有更改日志