exports 和 module.exports 有什么区别?
你一定很熟悉 Node.js 模块中的用来在你的模块中创建函数的 exports 对象,就像下面这样。
创建一个叫做 rocker.js
的文件:
1 | exports.name = function() { |
然后可以在另外一个文件中调用 rocker.js :
1 | var rocker = require('./rocker.js'); |
但是,module.exports
到底什么?它是合法的吗?
令人吃惊的是:module.exports
是真实存在的。exports
只不过是 module.exports
的帮手而已。你的模块直接返回返回 module.exports
给调用者,而不是 exports
。所有的 exports
做的工作实际上是收集属性,如果 module.exports
当前没有任何属性,exports
便将收集到的属性添加到 module.exports
上。如果 module.exports
已经存在若干属性,所以 exports
上的属性都会被忽略。
修改 rocker.js
文件:
1 | module.exports = 'ROCK IT!'; |
在另一个文件中调用 rocker.js
:
1 | var rocker = require('./rocker.js'); |
上述例子中的 rocker
模块完全将 exports.name
忽略了,只返回了一个 String 字符串:‘ROCK IT!’
。 从这个例子你大概明白了:你的模块并不一定总是一个模块的实例(module instance),它可以是任何合法的 JavaScript 对象——boolean, number, date, JSON, string, function, array 和其他的。你的模块可以是任何你设置的 module.exports
的值。如果你没有明确地为 module.exports
设置任何值,那么 exports
中的属性会自动添加到 module.exports
中,然后并返回它。
在这种情况下,你的模块是一个类:
1 | module.exports = function(name, age) { |
而你可以像这样使用:
1 | var Rocker = require('./rocker.js'); |
在这时候你的模块是一个数组:
1 | module.exports = [ |
而你可以这样使用:
1 | var rocker = require('./rocker.js'); |
现在你应该明白了点什么:
如果你想让你的模块返回一个特殊的对象类型,比如构造函数,那么你得使用 module.exports
;如果你只想模块作为一个典型的模块实例(module instance),那么就用exports
。
把属性添加到 module.exports
中和添加到 exports
中的结果是一样的。比如像这样:
1 | module.exports.name = function() { |
其实和下面的是一样的:
1 | exports.name = function() { |
但是要注意,他们不是同一个东西。就像之前说的一样,exports
只不过是 module.exports
的帮手而已。话虽如此,exports
还是推荐的对象,除非你想把你模块的对象类型从传统的模块实例(module instance)修改为其他的。
只要你没有使用赋值运算重写module.exports
对象,任何添加到 module.exports
和exports
的属性都能够在 require
模块中。
比如这是你的模块中的内容:
1 | module.exports.age = 68; |
下面的代码可以很好的工作:
1 | var rocker = require('./rocker.js'); |
但是,如果你在你的模块中重写了module.exports
中的任何地方,代码便会出错:
1 | module.exports = 'LOL'; |
或者这样:
1 | module.exports.age = 68; |
顺序没有关系,rocker.age
和 rocker.name
将显示为 undefined
。
并且注意:只是因为 module.exports
和 exports
都能输出模块,并不意味这你可以组合使用。我的建议是,坚持使用 exports.*
,明白module.exports
我希望这篇文章能帮助你理解exports
和module.exports
之间的不同,并且能进一步的理解模块在Node.js中是怎么工作的。