首页 > 代码库 > node.js module初步理解,exports与module.exports的区别

node.js module初步理解,exports与module.exports的区别

在开发一个复杂的应用程序的时候,我们需要把各个功能拆分、封装到不同的文件,在需要的时候引用该文件。没人会写一个几万行代码的文件,这样在可读性、复用性和维护性上都很差,几乎所有的编程语言都有自己的模块组织方式,比如Java中的包、C#中的程序集等,node.js使用模块和包来组织,其机制实现参照了CommonJS标准,虽未完全遵守,但差距不大,使用起来非常简单。

在node.js中模块与文件是一一对应的,也就是说一个node.js文件就是一个模块,文件内容可能是我们封装好的一些JavaScript方法、JSON数据、编译过的C/C++拓展等,在关于node.js的误会提到过node.js的架构


其中http、fs、net等都是node.js提供的核心模块,使用C/C++实现,外部用JavaScript封装。

创建、加载模块

在node.js中创建模块非常简单,一个文件就是一个模块,所以我们创建一个calc.js文件就创建了一个模块

var add = function(a,b){

        return a+b;

}

var minus = function(a,b){

        return a - b;

}

那么我们该如何调用呢? 用require调用。

在test.js中调用:

var calc = require("./calc");

console.log(calc.add(1,2));

可是调用了为什么会报错呢?   因为在calc.js中没有exports。

我们在calc.js中添加

exports.add = add;

exports.minus = minus;

重新运行:node  test.js    结果显示:3    没错了。


可是为什么从网上看到别人用module.exports 呢? 它和exports 有什么区别呢?是不是一样?

其实,Module.exports才是真正的接口,exports只不过是它的一个辅助工具。 最终返回给调用的是Module.exports而不是exports。

所有的exports收集到的属性和方法,都赋值给了Module.exports。当然,这有个前提,就是Module.exports本身不具备任何属性和方法。如果,Module.exports已经具备一些属性和方法,那么exports收集来的信息将被忽略。

修改calc.js 代码如下:

module.exports =‘ROCK IT!‘;

var add = function(a,b){

        return a+b;

}

var minus = function(a,b){

        return a - b;

}

exports.add = add;

exports.minus = minus;

再次运行:node  test.js

出现以下错误:

TypeError: Object ROCK IT! has no method ‘add‘

发现报错:对象“ROCK IT!”没有name方法 
rocker模块忽略了exports收集的name方法,返回了一个字符串“ROCK IT!”。由此可知,你的模块并不一定非得返回“实例化对象”。你的模块可以是任何合法的javascript对象--boolean, number, date, JSON, string, function, array等等。
你的模块可以是任何你设置给它的东西。如果你没有显式的给Module.exports设置任何属性和方法,那么你的模块就是exports设置给Module.exports的属性。
下面例子中,你的模块是一个类: 
代码如下:
module.exports = function(name, age) { 
this.name = name; 
this.age = age; 
this.about = function() { 
console.log(this.name +‘ is ‘+ this.age +‘ years old‘); 
}; 
}; 
 
可以这样应用它: 
代码如下:
var Singer = require(‘./singer.js‘); 
var r = new Singer(‘Bao‘, 62); 
r.about(); // Bao is 62 years old 
 
下面例子中,你的模块是一个数组: 
代码如下:
module.exports = [‘Bao ‘, ‘Liu Dehua‘, ‘Zhou Jielun‘, ‘Lin Junjie‘, ‘Allen Iverson‘]; 
 
可以这样应用它: 
代码如下:
var rocker = require(‘./singer.js‘); 
console.log(‘Singer a great singer: ‘ + rocker[2]); //Singer a greate singer: Zhou Jielun

module.exports与exports

事实的情况是这样的,其实module.exports才是模块公开的接口,每个模块都会自动创建一个module对象,对象有一个modules的属性,初始值是个空对象{},module的公开接口就是这个属性——module.exports。既然如此那和exports对象有什么关系呢?为什么我们也可以通过exports对象来公开接口呢?

为了方便,模块中会有一个exports对象,和module.exports指向同一个变量,所以我们修改exports对象的时候也会修改module.exports对象,这样我们就明白网上盛传的module.exports对象不为空的时候exports对象就自动忽略是怎么回事儿了,因为module.exports通过赋值方式已经和exports对象指向的变量不同了,exports对象怎么改和module.exports对象没关系了。

一次加载

无论调用多少次require,对于同一模块node.js只会加载一次,引用多次获取的仍是相同的实例,看个例子

test.js

var name=‘‘;

function setName(n){
    name=n;
} 

function printName(){
    console.log(name);
}

exports.setName=setName;
exports.printName=printName;

 

index.js

var test1=require(‘./test‘),
    test2=require(‘./test‘);

test1.setName(‘Bao Zhiqiang‘);
test2.printName();

require搜索module方式

node.js中模块有两种类型:核心模块和文件模块,核心模块直接使用名称获取,比如最长用的http模块

var http=require(‘http‘); 

在上面例子中我们使用了相对路径 ‘./test‘来获取自定义文件模块,那么node.js有几种搜索加载模块方式呢?

  1. 核心模块优先级最高,直接使用名字加载,在有命名冲突的时候首先加载核心模块文件模块只能按照路径加载(可以省略默认的.js拓展名,不是的话需要显示声明书写)
    1. 绝对路径
    2. 相对路径
  2. 查找node_modules目录,我们知道在调用 npm install <name> 命令的时候会在当前目录下创建node_module目录(如果不存在) 安装模块,当 require 遇到一个既不是核心模块,又不是以路径形式表示的模块名称时,会试图 在当前目录下的 node_modules 目录中来查找是不是有这样一个模块。如果没有找到,则会 在当前目录的上一层中的 node_modules 目录中继续查找,反复执行这一过程,直到遇到根 目录为止。

注:本文章是参考别人的博客,加上自己的理解写的,本人奔着学习的目的写的。如果文中内容有雷同的,请不要介意。本人并没有抄袭的意思,目的是记录传播知识。

参考文献:
1、

node.js module初步理解  http://www.cnblogs.com/dolphinX/p/3485260.html

2、
nodejs中exports与module.exports的区别详细介绍  http://www.2cto.com/kf/201302/191594.html