兩者的關(guān)系
exports = module.exports = {}
nodejs如何導(dǎo)出模塊
當(dāng)不改變module.exports和exports的指向,module.exports和exports均可導(dǎo)出迅栅,因?yàn)樯厦嬉舱f(shuō)過(guò)這兩個(gè)對(duì)象實(shí)際上指向的都是同一個(gè)堆地址
不改變指向
//a.js
exports.a = 1;
module.exports.b = 2;
//b.js
const test = require('./a');
console.log(test);
//output: { a: 1, b: 2 }
改變指向:僅module.exports可導(dǎo)出
//a.js
exports.a = 1; // exports = { a: 1 };
module.exports = { b: 2 };
//b.js
const test = require('./a');
console.log(test);
//output: { b: 2 }
刨根問(wèn)底
為什么指向一旦被改變exports就不能導(dǎo)出對(duì)象了呢殖卑?
首先node加載模塊的時(shí)候會(huì)用一個(gè)函數(shù)將代碼包裹
(function (exports, require, module, __filename, __dirname) {
});
當(dāng)加載模塊的時(shí)候,module已經(jīng)被當(dāng)作參數(shù)傳入珠增,所以即使module.exports的值改變也能夠在其他文件中進(jìn)行調(diào)用,也就是為什么指向一旦被改變,只有module.exports可導(dǎo)出挟秤。
那么有同學(xué)問(wèn)了exports也被當(dāng)作參數(shù)傳入啦~~~
利用node-inspector調(diào)試我們可以找到答案
Module.prototype._compile = function(content, filename) {
......
let result;
//這里傳入的exports其實(shí)是this.exports,而當(dāng)前this是指向module的抄伍,所以加載模塊傳入的exports其實(shí)是module.exports艘刚,所以一旦改變了exports指向,就不能通過(guò)exports導(dǎo)出了
const exports = this.exports;
const thisValue = exports;
const module = this;
if (requireDepth === 0) statCache = new SafeMap();
if (inspectorWrapper) {
result = inspectorWrapper(compiledWrapper, thisValue, exports,
require, module, filename, dirname);
} else {
result = ReflectApply(compiledWrapper, thisValue,
[exports, require, module, filename, dirname]);
}
hasLoadedAnyUserCJSModule = true;
if (requireDepth === 0) statCache = null;
return result;
};
為什么module.exports = {}的多次導(dǎo)出和module.exports.x的多次導(dǎo)出結(jié)果不一樣截珍?
其實(shí)也就是值拷貝的原理
//b.js
module.exports = {
count: 1,
};
setTimeout(() => {
module.exports = {
count: 2,
};
}, 100);
//a.js
const b = require("./b");
console.log(b); //{count: 1}
setTimeout(() => {
console.log(b); //{count: 1}
}, 200);
翻譯一下
setTimeout(() => {
myModule.exports = {
count: 2,
};
}, 100);
const myModule = {
exports: {},
};
myModule.exports = {
count: 1,
};
const myObj = myModule.exports;
//const b = require('./b');
//myModule.exports導(dǎo)出的對(duì)象直接賦值給了新對(duì)象myObj攀甚,兩個(gè)對(duì)象指向同一個(gè)堆地址
//這時(shí)候重新賦值myModule.exports對(duì)象(改變了myModule.exports的地址指向),當(dāng)然不會(huì)影響新對(duì)象myObj
console.log(myObj); //{count: 1}
setTimeout(() => {
console.log(myObj); //{count: 1}
}, 200);
改變一下b.js
//b.js
module.exports.count = 1;
setTimeout(() => {
module.exports.count = 2;
}, 200);
//a.js
const b = require("./b");
console.log(b.count); //{count: 1}
setTimeout(() => {
console.log(b.count); //{count: 2}
}, 200);
翻譯一下
setTimeout(() => {
myModule.exports.count = 2;
}, 100);
const myModule = {
exports: {},
};
myModule.exports.count = 1;
const myObj = myModule.exports;
//const b = require('./b');
//myModule.exports導(dǎo)出的對(duì)象直接賦值給了新對(duì)象myObj岗喉,兩個(gè)對(duì)象指向同一個(gè)堆地址
//這時(shí)候改變myModule.exports.count對(duì)象(兩個(gè)對(duì)象仍然指向同一個(gè)堆地址)云稚,結(jié)果自然會(huì)改變
console.log(myObj); //{count: 1}
setTimeout(() => {
console.log(myObj); //{count: 2}
}, 200);
所以為什么我們?cè)趯?dǎo)出模塊時(shí)建議使用myModule.exports = {}導(dǎo)出,以避免預(yù)期之外的情況