深入理解node.js的module.export 和 export方法的区别
你肯定非常熟悉nodejs模块中的exports对象,你可以用它创建你的模块。例如:(假设这是rocker.js文件)
exports.name = function() {
console.log('My name is Lemmy Kilmister');
};
在另一个文件中你这样引用
var rocker = require('./rocker.js');
rocker.name(); // 'My name is Lemmy Kilmister'
那到底Module.exports是什么呢?它是否合法呢?
其实,Module.exports才是真正的接口,exports只不过是它的一个辅助工具。 最终返回给调用的是Module.exports而不是exports。
所有的exports收集到的属性和方法,都赋值给了Module.exports。当然,这有个前提,就是Module.exports本身不具备任何属性和方法。如果,Module.exports已经具备一些属性和方法,那么exports收集来的信息将被忽略。
修改rocker.js如下:
module.exports = 'ROCK IT!';
exports.name = function() {
console.log('My name is Lemmy Kilmister');
};
再次引用执行rocker.js
var rocker = require('./rocker.js');
rocker.name(); // TypeError: Object ROCK IT! has no method 'name'
发现报错:对象“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 Rocker = require('./rocker.js');
var r = new Rocker('Ozzy', 62);
r.about(); // Ozzy is 62 years old
下面例子中,你的模块是一个数组:
module.exports = ['Lemmy Kilmister', 'Ozzy Osbourne', 'Ronnie James Dio', 'Steven Tyler', 'Mick Jagger'];
可以这样应用它:
var rocker = require('./rocker.js');
console.log('Rockin in heaven: ' + rocker[2]); //Rockin in heaven: Ronnie James Dio
现在你明白了,如果你想你的模块是一个特定的类型就用Module.exports。如果你想的模块是一个典型的“实例化对象”就用exports。
给Module.exports添加属性类似于给exports添加属性。例如:
module.exports.name = function() {
console.log('My name is Lemmy Kilmister');
};
同样,exports是这样的
exports.name = function() {
console.log('My name is Lemmy Kilmister');
};
请注意,这两种结果并不想同。前面已经提到module.exports是真正的接口,exports只不过是它的辅助工具。推荐使用exports导出,除非你打算从原来的“实例化对象”改变成一个类型
Node.js在模块编译的过程中会对模块进行包装,最终会返回类似下面的代码:
(function (exports, require, module, __filename, __dirname) {
// module code...
});
其中,module就是这个模块本身,require是对Node.js实现查找模块的模块Module._load实例的引用,__filename和__dirname是Node.js在查找该模块后找到的模块名称和模块绝对路径,这就是官方API里头这两个全局变量的来历。
关于module.exports与exorts的区别,了解了下面几点之后应该就完全明白:
模块内部大概是这样:
exports = module.exports = {};
exports是module.exports的一个引用
require引用模块后,返回给调用者的是module.exports而不是exports
exports.xxx,相当于在导出对象上挂属性,该属性对调用模块直接可见exports =相当于给exports对象重新赋值,调用模块不能访问exports对象及其属性如果此模块是一个类,就应该直接赋值
module.exports,这样调用者就是一个类构造器,可以直接new实例
客官如果看明白咋回事儿了下面的内容可以忽略:)
假如有模块a.js代码如下:
exports.str = 'a';
exports.fn = function() {};
对a模块的调用:
var a = require('./a');
console.log(a.str);
console.log(a.fn());
这样用是对的,如果改造a如下:
exports.str = 'a';
exports = function fn() {};
在调用a模块时自然没用fn属性了。
再改造下a模块:
exports.str = 'a';
module.exports = function fn() {};
这时a模块其实就是fn函数的引用,也就是说可以require('./a')()这样使用,而同时不再有str属性了。
下面直接导出一个类:
module.exports = function A() {};
调用:
var A = require('./a');
var a = new A();
总结下,有两点:
对于要导出的属性,可以简单直接挂到
exports对象上对于类,为了直接使导出的内容作为类的构造器可以让调用者使用new操作符创建实例对象,应该把构造函数挂到
module.exports对象上,不要和导出属性值混在一起
很多新手可能会迷惑于 exports 和 module.exports 的区别,为了更好的理解 exports 和 module.exports 的关系,我们先来巩固下 js 的基础。示例:
test.js
var a = {name: 1};
var b = a;
console.log(a);
console.log(b);
b.name = 2;
console.log(a);
console.log(b);
var b = {name: 3};
console.log(a);
console.log(b);
运行 test.js 结果为:
{ name: 1 }
{ name: 1 }
{ name: 2 }
{ name: 2 }
{ name: 2 }
{ name: 3 }
解释:a 是一个对象,b 是对 a 的引用,即 a 和 b 指向同一块内存,所以前两个输出一样。当对 b 作修改时,即 a 和 b 指向同一块内存地址的内容发生了改变,所以 a 也会体现出来,所以第三四个输出一样。当 b 被覆盖时,b 指向了一块新的内存,a 还是指向原来的内存,所以最后两个输出不一样。
明白了上述例子后,我们只需知道三点就知道 exports 和 module.exports 的区别了:
- module.exports 初始值为一个空对象 {}
- exports 是指向的 module.exports 的引用
- require() 返回的是 module.exports 而不是 exports
Node.js 官方文档的截图证实了我们的观点:

exports = module.exports = {...}
我们经常看到这样的写法:
exports = module.exports = {...}
上面的代码等价于:
module.exports = {...}
exports = module.exports
原理很简单:module.exports 指向新的对象时,exports 断开了与 module.exports 的引用,那么通过 exports = module.exports 让 exports 重新指向 module.exports。
深入理解node.js的module.export 和 export方法的区别的更多相关文章
- 理解Node.js的事件轮询
前言 总括 : 原文地址:理解Node.js的事件轮询 Node小应用:Node-sample 智者阅读群书,亦阅历人生 正文 Node.js的两个基本概念 Node.js的第一个基本概念就是I/O操 ...
- 方便大家学习的Node.js教程(一):理解Node.js
理解Node.js 为了理解Node.js是如何工作的,首先你需要理解一些使得Javascript适用于服务器端开发的关键特性.Javascript是一门简单而又灵活的语言,这种灵活性让它能够经受住时 ...
- 深入理解Node.js中的垃圾回收和内存泄漏的捕获
深入理解Node.js中的垃圾回收和内存泄漏的捕获 文章来自:http://wwsun.github.io/posts/understanding-nodejs-gc.html Jan 5, 2016 ...
- Node.Js的Module System 以及一些常用 Module
Node.Js学习就按照这本书的流程来. 在第7章结束与第10章结束时分别自己出一个小项目练练手.Node.Js的入门学习计划是这样. 目录:, QQ:1045642972 欢迎来索书以及讨论Node ...
- 如何理解Node.js和JavaScript的关系
一.Javascript的引擎 浏览器一般有两个引擎,一个是Html引擎,一个是脚本引擎. JavaScript是一种脚本语言,最初用于浏览器的动态显示,方便操作页面数据和内容.但实际上,它也可以在浏 ...
- 使用Node.js给图片加水印的方法
一.准备工作: 首先,确保你本地已经安装好了node环境. 然后,我们进行图像编辑操作需要用到一个Node.js的库:images. 这个库的地址是:https://github.com/zhangy ...
- JavaScript进阶(七)JS截取字符串substr 和 substring方法的区别
JS截取字符串substr 和 substring方法的区别 substr方法 返回一个从指定位置开始的指定长度的子字符串. stringvar.substr(start [, length ]) 参 ...
- js正则表达式中test,exec,match方法的区别说明
js正则表达式中test,exec,match方法的区别说明 test test 返回 Boolean,查找对应的字符串中是否存在模式.var str = "1a1b1c";var ...
- JS ,substr、 substring、charAt方法的区别
JS 截取字符串substr 和 substring方法的区别,需要的朋友可以参考下,根据需要自行选择. substr 方法 返回一个从指定位置开始的指定长度的子字符串. stringvar.subs ...
随机推荐
- shell中如何取括号中的字符
1. 使用grep(结果带括号,不知道有没有办法仅把括号中的内容匹配出来) $a='abc[edg]adfirpqu' $echo $a|grep -o '\[.*\]' #中括号的处理需要转义 [e ...
- 算法笔记_110:第四届蓝桥杯软件类省赛真题(JAVA软件开发高职高专组部分习题)试题解答
目录 1 猜年龄 2 组素数 3 第39级台阶 4 核桃的数量 5 打印十字图 6 买不到的数目 前言:以下试题解答代码部分仅供参考,若有不当之处,还请路过的同学提醒一下~ 1 猜年龄 标题: ...
- 网站中超链接方式直接添加QQ好友
使用情景: 在图中点击图片,会弹出添加qq好友的窗口进行好友添加. 链接如下: tencent://AddContact/?fromId=45&fromSubId=1&subcmd=a ...
- HDU 1023 Train Problem II 大数打表Catalan数
一个出栈有多少种顺序的问题.一般都知道是Catalan数了. 问题是这个Catalan数非常大,故此须要使用高精度计算. 并且打表会速度快非常多.打表公式要熟记: Catalan数公式 Cn=C(2n ...
- redission 分布式锁
https://my.oschina.net/haogrgr/blog/469439 分布式锁和Redisson实现 Aug 20, 2017 CONTENTS 概述 分布式锁特性 Redis实现 ...
- 修改模块搜索路径(注意:py运行完毕,就会失效,他只是临时产生效果)
# -*- coding: utf-8 -*- #python 27 #xiaodeng #修改模块搜索路径(注意:py运行完毕,就会失效,他只是临时产生效果) import sys print sy ...
- Tomcat 签名认证配置简例
在项目的web.xml中增加 <security-constraint> <web-resource-collection> <web-resource-name> ...
- 史上最全的iOS面试题及答案,且看且珍藏,错过就没有喽!
1. Object-c的类可以多重继承么?可以实现多个接口么?Category是什么?重写一个类的方式用继承好还是分类好?为什么? 答:Object-c的类不可以多重继承;可以实现多个接口,通过实现多 ...
- Ant build xml中的各种变量解释
Ant build.xml中的各种变量 Ant环境变量分为四种: 1. build.properties文件中定义的变量 2. build.xml文件中定义的变量, 3. ...
- 漫谈Github与开源,Git介绍以及Git的思想和基本工作原理 Git工作流程
漫谈Github与开源 文字亮点: 为什么这些优秀的工程师会开源自己的项目? 因为开源是一种精神. 无数的软件开发者苦心积虑保护自己的代码不被破解,而还是被聪明绝顶的脚本小子破解了,但破解无数软件的脚 ...