Node.js module.exports 的用途是啥,你如何使用它?

Posted

技术标签:

【中文标题】Node.js module.exports 的用途是啥,你如何使用它?【英文标题】:What is the purpose of Node.js module.exports and how do you use it?Node.js module.exports 的用途是什么,你如何使用它? 【发布时间】:2014-02-21 17:20:47 【问题描述】:

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

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

根据Node.js documentation:

模块

对当前的引用 module。特别是module.exports 与导出对象相同。看 src/node.js 了解更多信息。

但这并没有真正的帮助。

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

【问题讨论】:

另见:javascript - module.exports vs exports in Node.js - Stack Overflow 【参考方案1】:
let test = function() 
    return "Hello world"
;
exports.test = test;

【讨论】:

这与接受答案中的第一个 sn-p 类似(return "Hello world" 没有区别),但没有任何解释。请在回答之前确保您的回答会为主题添加一些内容。【参考方案2】:

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

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

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 对象的名称不必与您要添加的值的模块内部范围名称相同,因此您可以:

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

接着是:

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

【讨论】:

好答案 - 在我看来,“暴露”是比“出口”更好的术语选择 @ApopheniaOverload - 您可以执行“exports.func1、exports.func2 等”以从一个文件中获得多个公开的方法。 模块 require 应该是 var m = require('./mymodule');,带有点和斜线。这样 Node.js 就知道我们正在使用本地模块。 请务必使用:require('./module_name') 语法,因为可能有一些其他 node.js 模块具有某些名称,而不是选择您自己的模块,它会选择一个与 node.js 一起安装的 @UpTheCreek 将模块公开的公共符号称为“导出”的传统由来已久,这可以追溯到许多编程系统和几十年。这不是 Node 开发人员发明的新术语。【参考方案3】:

意图是:

模块化编程是一种软件设计技术,它强调 将程序的功能分离为独立的, 可互换的模块,每个模块都包含必要的一切 只执行所需功能的一个方面。

Wikipedia

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

试试这个例子:

fileLog.js

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

module.exports = log;

stdoutLog.js

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

module.exports = log;

program.js

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

log('hello world!');

执行

$ 节点程序.js

世界你好!

现在尝试将 ./stdoutLog.js 替换为 ./fileLog.js

【讨论】:

【参考方案4】:

模块系统的目的是什么?

它完成了以下事情:

    防止我们的文件膨胀到非常大的尺寸。有文件,例如其中 5000 行代码在开发过程中通常很难处理。 强制分离关注点。将我们的代码拆分为多个文件允许我们为每个文件使用适当的文件名。这样我们就可以轻松识别每个模块的功能以及在哪里找到它(假设我们创建了一个逻辑目录结构,这仍然是您的责任)。

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

它是如何工作的?

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

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

示例:

test1.js

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

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

test2.js

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

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

其他有用的知识:

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

【讨论】:

【参考方案5】:

请注意,NodeJS 模块机制基于 CommonJS 模块,这些模块在许多其他实现(如 RequireJSSproutCoreCouchDBWakandaOrientDBArangoDBRingoJSTeaJSSilkJScurl.js,甚至是 Adobe Photoshop(通过 PSLib)。 你可以找到已知实现的完整列表here。

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

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

(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 and CommonJS modules (caching and circular references))

【讨论】:

【参考方案6】:

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

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

Exports 正好相反,你正在创建你想要的模块,比如说模块 add.js 并将该模块放入 node.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');

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

【讨论】:

是出口还是出口? 感谢您的帮助:)【参考方案7】:

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

我有一个关于 module_export 的视频here

【讨论】:

【参考方案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");

【讨论】:

谢谢如果对你有帮助请采纳我的回答:) 派对朋友有点晚了 :) @BenTaliadoros 我也认为他迟到了,我也认为他的 anyVariable 对象有很多错误。上面的行 sayHelloInSpanish 方法不应以分号 (;) 结尾,并且 sayHelloInSpanish = function 是错误的。这个对象的所有事情都是错误的。我会编辑他的答案 编辑被禁用。 alphadogg 在这个答案中还编辑了什么?? 只是格式化。除非它是我没有遇到过的一些疯狂的 es6 东西,而且我确定它不是,否则它根本不是有效的 JS【参考方案9】:

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

在编写模块时记住

模块加载被缓存,只有初始调用会评估 JavaScript。 可以在模块内使用局部变量和函数,而不是所有内容都需要导出。 module.exports 对象也可用作exports 简写。但是当返回一个单独的函数时,总是使用module.exports

根据:"Modules Part 2 - Writing modules".

【讨论】:

【参考方案10】:

如果将新对象的引用分配给exports 和/或modules.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。棘手的后果。如果同时更改对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.exports 设置为什么,都将始终是导出的内容。 export 变量只是 module.exports 的局部变量别名(好像let exports = module.exports 是每个模块的第一行)。永远不应重新分配此变量 - 这样做只会导致您失去该别名,并且不会更改导出的内容。即exports = 'abc' 不会导出“abc”。【参考方案11】:

参考链接是这样的:

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

exportsmodule.exports的属性,如函数或变量,会暴露在外面

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

为什么?

因为exports只是module.exports的引用,你可以在exports中添加属性,但是如果你覆盖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的属性对外是私有的。

【讨论】:

【参考方案12】:

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

您可以同时使用exportsmodule.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

简单来说,您可以将所需文件视为返回单个对象的函数,您可以通过在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 之间的区别在 this answer here 中有更好的解释。

【讨论】:

我怎样才能调用 require 来自其他文件夹的一些模块,这些模块没有我的某个根文件夹? @user301639 您可以使用相对路径来遍历文件系统层次结构。 require 相对于您在其中执行 node app.js 的文件夹开始。我建议您发布一个带有显式代码+文件夹结构示例的新问题,以获得更清晰的答案。 我不得不调整您的 module.exports 示例以使其正常工作。文件:var sayHello = require('./ex6_module.js'); console.log(sayHello()); 和模块:module.exports = exports = function() return "Hello World!"; 发现增量示例非常好,每当我对导出的操作感到超负荷时,我都会用它来刷新我的想法。 module.exports = exports = function()... 第二个exports 只是一个变量,对吧?也就是说,可以是module.exports = abc = function()

以上是关于Node.js module.exports 的用途是啥,你如何使用它?的主要内容,如果未能解决你的问题,请参考以下文章

Node.js中exports与module.exports的区别

在 Node.js 中声明多个 module.exports

Node.js module.exports和exports的区别

node.js exports module.exports

node.js 中 module.exports= 函数的含义

Node.js的学习入门(module.exports与exports)