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

时间:2011-03-15 11:56:11

标签: javascript node.js

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

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

根据Node.js documentation

  

模块

     

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

但这并没有真正帮助。

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

12 个答案:

答案 0 :(得分:1534)

module.exportsrequire调用后实际返回的对象。

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

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

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

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

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

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

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


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

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

接下来是:

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

答案 1 :(得分:209)

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

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

简单来说,您可以将所需文件视为返回单个对象的函数,并且可以通过在{{1}上设置它们来向返回的对象添加属性(字符串,数字,数组,函数,任何内容)。 }。

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

(sayhello.js):

module.exports

(app.js):

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

this answer here中更好地解释了exports和module.exports之间的区别。

答案 2 :(得分:59)

请注意,NodeJS模块机制基于CommonJS模块,这些模块在 RequireJS 等许多其他实现中都受支持,但 SproutCore CouchDB Wakanda OrientDB ArangoDB RingoJS TeaJS SilkJS curl.js ,甚至 Adob​​e 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)

答案 3 :(得分:32)

如果您将对象的引用分配给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() {}; 

答案 4 :(得分:28)

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

enter image description here

我有一个关于module_export的视频here

答案 5 :(得分:17)

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

记住编写模块时

  • 缓存模块加载,只有初始调用评估JavaScript。
  • 可以在模块中使用局部变量和函数,而不是所有东西都需要导出。
  • module.exports对象也可以exports速记显示。但是当返回单一函数时,请始终使用module.exports
  

module exports diagram

根据:"Modules Part 2 - Writing modules"

答案 6 :(得分:8)

引用链接是这样的:

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

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的属性对于外部是私有的。

答案 7 :(得分:5)

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

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

导出 正好相反,您正在创建所需的模块,让我们说模块addition.js并将该模块放入节点.js,你是通过导出它来实现的。

在此写任何内容之前,请记住, module.exports.additionTwo exports.additionTwo

相同 嗯,这就是原因,我们喜欢

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

小心路径

假设您已创建了add.js模块,

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

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

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

这会错误地说

  

错误:找不到模块addition.js

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

设置路径
set NODE_PATH = path/to/your/additon.js

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

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

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

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

答案 8 :(得分:2)

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

假设有一个包含两个函数的文件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");

答案 9 :(得分:1)

目的是:

  

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

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

<强> 执行

  

$ node program.js

     

你好世界!

现在尝试为 ./ fileLog.js 交换 ./ stdoutLog.js

答案 10 :(得分:0)

  

模块系统的目的是什么?

它完成以下任务:

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

具有模块使查找代码的某些部分更加容易,这使我们的代码更具可维护性。

  

它如何工作?

NodejS使用以下列方式工作的CommomJS模块系统:

  1. 如果文件要导出内容,则必须使用module.export语法对其进行声明
  2. 如果文件要导入内容,则必须使用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')};

其他有用的知识:

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

答案 11 :(得分:-1)

let test = function() {
    return "Hello world"
};
exports.test = test;