我在Node.js模块中找到了以下契约:
module.exports = exports = nano = function database_module(cfg) {...}
我想知道module.exports和exports之间有什么区别,为什么在这里使用它们。
我在Node.js模块中找到了以下契约:
module.exports = exports = nano = function database_module(cfg) {...}
我想知道module.exports和exports之间有什么区别,为什么在这里使用它们。
当前回答
让我们用两种方式创建一个模块:
单向
var aa = {
a: () => {return 'a'},
b: () => {return 'b'}
}
module.exports = aa;
第二种方式
exports.a = () => {return 'a';}
exports.b = () => {return 'b';}
这就是require()集成模块的方式。
第一种方式:
function require(){
module.exports = {};
var exports = module.exports;
var aa = {
a: () => {return 'a'},
b: () => {return 'b'}
}
module.exports = aa;
return module.exports;
}
第二种方式
function require(){
module.exports = {};
var exports = module.exports;
exports.a = () => {return 'a';}
exports.b = () => {return 'b';}
return module.exports;
}
其他回答
为什么这两个都用在这里
我相信他们只是想清楚module.exports、exports和nano指向同一个函数——允许您使用任意一个变量来调用文件中的函数。nano为函数的功能提供了一些上下文。
导出不会被导出(只有module.exports会被导出),那么为什么还要麻烦覆盖它呢?
冗长的权衡限制了未来错误的风险,例如在文件中使用导出而不是module.exports。它还澄清了module.exports和exports实际上指向相同的值。
module.exports与导出
只要不重新分配module.exports或exports(而是向它们都引用的对象添加值),就不会有任何问题,并且可以安全地使用exports以更简洁。
当将其中一个分配给非对象时,它们现在指向不同的位置,这可能会令人困惑,除非您有意希望module.exports是特定的(例如函数)。
将导出设置为非对象没有多大意义,因为必须在末尾设置module.exports=导出,才能在其他文件中使用它。
let module = { exports: {} };
let exports = module.exports;
exports.msg = 'hi';
console.log(module.exports === exports); // true
exports = 'yo';
console.log(module.exports === exports); // false
exports = module.exports;
console.log(module.exports === exports); // true
module.exports = 'hello';
console.log(module.exports === exports); // false
module.exports = exports;
console.log(module.exports === exports); // true
为什么将module.exports分配给函数?
更简洁!比较第二个示例的长度:
helloWorld1.js:module.exports.hello = () => console.log('hello world');
app1.js:let sayHello=require('./helloWorld1');sayHellohello;//你好,世界
helloWorld2.js:module.exports = () => console.log('hello world');
app2.js:let sayHello=require('./helloWorld2');说你好;//你好,世界
在节点js中,module.js文件用于运行module.load系统。每当节点执行文件时,它都会将js文件内容包装如下
'(function (exports, require, module, __filename, __dirname) {',+
//your js file content
'\n});'
由于这种包装在urjs源代码中,您可以访问导出、require、模块等。。之所以使用这种方法,是因为没有其他方法可以将js文件中的功能写入另一个文件。
然后节点使用c++执行这个包装函数。此时,传递到此函数的exports对象将被填充。
您可以在这个函数中看到参数导出和模块。实际上,exports是模块构造函数函数的公共成员。
查看以下代码
将此代码复制到b.js中
console.log("module is "+Object.prototype.toString.call(module));
console.log("object.keys "+Object.keys(module));
console.log(module.exports);
console.log(exports === module.exports);
console.log("exports is "+Object.prototype.toString.call(exports));
console.log('----------------------------------------------');
var foo = require('a.js');
console.log("object.keys of foo: "+Object.keys(foo));
console.log('name is '+ foo);
foo();
将此代码复制到.js
exports.name = 'hello';
module.exports.name = 'hi';
module.exports.age = 23;
module.exports = function(){console.log('function to module exports')};
//exports = function(){console.log('function to export');}
现在使用节点运行
这是输出
module is [object Object]
object.keys id,exports,parent,filename,loaded,children,paths
{}
true
导出为[对象对象]
foo的object.keys:名称是函数(){console.log('模块导出函数')}功能到模块导出
现在删除.js中的注释行,并注释该行上方的行然后删除b.js的最后一行并运行。
在javascript世界中,不能重新分配作为参数传递的对象,但当该函数的对象设置为另一个函数的参数时,可以更改该函数的公共成员
请记住
仅当您想在使用require关键字时获取函数时,才使用module.exports。在上面的例子中,我们varfoo=require(a.js);你可以看到我们可以将foo作为函数调用;
节点文档就是这样解释的导出对象是由Module系统创建的。有时这是不可接受的,许多人希望自己的模块是某个类的实例。为此,请将所需的导出对象分配给Module.exports
尽管这个问题早就得到了回答和接受,但我只想分享我的2分钱:
您可以想象,在文件的开头有这样的内容(仅供解释):
var module = new Module(...);
var exports = module.exports;
因此,无论您做什么,只要记住,当您从其他地方需要模块时,模块将返回module.exports和NOT导出。
所以,当你做如下事情时:
exports.a = function() {
console.log("a");
}
exports.b = function() {
console.log("b");
}
您正在向module.exports指向的对象添加2个函数a和b,因此返回结果的类型将是一个对象:{a:[Function],b:[Function〕}
当然,如果在本例中使用module.exports而不是exports,则会得到相同的结果。
在这种情况下,您希望module.exports的行为类似于导出值的容器。然而,如果您只想导出构造函数,那么对于使用module.exports或exports,您应该知道一些事情;(请再次记住,当您需要某些东西时,会返回module.exports,而不是export)。
module.exports = function Something() {
console.log('bla bla');
}
现在返回结果的类型是“function”,您可以要求它并立即调用,如:var x=require('./file1.js')();因为您将返回结果覆盖为函数。
但是,使用导出时不能使用以下内容:
exports = function Something() {
console.log('bla bla');
}
var x = require('./file1.js')(); //Error: require is not a function
因为对于导出,引用不再指向module.exports所指向的对象,因此导出和module.export之间不再存在关系。在这种情况下,module.exports仍然指向将返回的空对象{}。
另一个主题的公认答案也应有助于:JavaScript是否通过引用传递?
要了解这些差异,您必须首先了解Node.js在运行时对每个模块做了什么。Node.js为每个模块创建一个包装函数:
(function(exports, require, module, __filename, __dirname) {
})()
注意,第一个param导出是一个空对象,第三个param模块是一个具有许多财产的对象,其中一个财产名为exports。这是导出的来源和module.exports的来源。前者是变量对象,后者是模块对象的属性。
在模块中,Node.js在开始时自动执行以下操作:module.exports=exports,并最终返回module.exports。
因此,您可以看到,如果您为导出重新分配一些值,那么它不会对module.exports产生任何影响
let exports = {};
const module = {};
module.exports = exports;
exports = { a: 1 }
console.log(module.exports) // {}
但是如果你更新导出的财产,它肯定会对module.exports产生影响。因为它们都指向同一个对象。
let exports = {};
const module = {};
module.exports = exports;
exports.a = 1;
module.exports.b = 2;
console.log(module.exports) // { a: 1, b: 2 }
还要注意,如果您将另一个值重新分配给module.exports,那么对于导出更新来说似乎没有意义。由于module.exports指向另一个对象,因此忽略导出上的每个更新。
let exports = {};
const module = {};
module.exports = exports;
exports.a = 1;
module.exports = {
hello: () => console.log('hello')
}
console.log(module.exports) // { hello: () => console.log('hello')}
这显示了require()如何以最简单的形式工作,摘自Eloquent JavaScript
问题模块不可能直接导出导出对象以外的值,例如函数。例如,模块可能只希望导出其定义的对象类型的构造函数。现在,它无法做到这一点,因为require始终使用它创建的导出对象作为导出值。
解决方案为模块提供另一个变量module,它是一个具有属性导出的对象。此属性最初指向require创建的空对象,但可以用另一个值覆盖以导出其他对象。
function require(name) {
if (name in require.cache)
return require.cache[name];
var code = new Function("exports, module", readFile(name));
var exports = {}, module = {exports: exports};
code(exports, module);
require.cache[name] = module.exports;
return module.exports;
}
require.cache = Object.create(null);