1、場景如下
例如a.js和b.js相互引用:
a.js內容為:
exports.done = false;
var b = require('./b.js');
exports.hello = false;
console.log(`在a中b.done的值為:`+b.done);
exports.done = true;複製程式碼
b.js內容為:
exports.done = false;
var a = require('./a.js');
console.log(`在b中a.done的值為:`+a.done);
console.log(`在b中a.hello的值為:`+a.hello);
exports.done = true;複製程式碼
在main.js引用a和b
var a = require('./a.js');
var b = require('./b.js');複製程式碼
問題如下
1、main.js引用了a.js,此時讀取a.js裡的資料
2、a.js執行時先把exports.done的值設為false,接著執行第二行,第二行開始載入b.js
3、b.js執行時,先把自己的exports.done的值設為false,然後載入a.js
問題來了,此時a.js是不是又開始載入,然後又遇到b.js,b.js載入又遇到a.js。。。。無限迴圈呢複製程式碼
結果肯定是不會無限迴圈,我們先把結果理出來,然後分析require函式裡面什麼樣的功能避免了無限迴圈引用
結果如下:
1、main.js引用了a.js,此時讀取a.js裡的資料
2、a.js執行時先把exports.done的值設為false,接著執行第二行,第二行開始載入b.js
3、b.js執行時,先把自己的exports.done的值設為false,然後載入a.js
4、此時的a.js不會重新載入檔案,而是匯出一個物件,{ exports: done }(其它屬性略去)
5、所以在b.js裡面var a = { exports: done }(其它屬性略去),然後繼續執行
6、然後b.js console出兩行分別是
在b中a.done的值為:false
在b中a.hello的值為:undefined7、最後b.js把exports.done屬性的值設為了true
8、接著回到a.js的第二行,var b = require('./b.js');此時b={ exports: true}(其它屬性略去)
9、接著執行第三行exports.hello = false;,此時hello屬性為false
10、執行第四行,列印出
在a中b.done的值為:true11、接著執行最後一行exports.done = true; 把自己的done屬性又改為true複製程式碼
這裡最讓人困惑的就是上面的第四條,就是為什麼a.js不會重新載入,而是匯出一個物件呢
這是我之前轉載的文章,關於require函式原始碼juejin.im/post/5c7b6e…
其中require你相當於看成如下函式(只關心第三步和第四步)
Module._load = function(request, parent, isMain) {
// 計算絕對路徑
var filename = Module._resolveFilename(request, parent);
// 第一步:如果有快取,取出快取
var cachedModule = Module._cache[filename];
if (cachedModule) {
return cachedModule.exports;
// 第二步:是否為內建模組
if (NativeModule.exists(filename)) {
return NativeModule.require(filename);
}
// 第三步:生成模組例項,存入快取
var module = new Module(filename, parent);
/**注:Module實現如下
*function Module(id, parent) { *this.id = id;
*this.exports = {};
*this.parent = parent;
*this.filename = null;
*this.loaded = false;
*this.children = [];
*}
**/ Module._cache[filename] = module;
// 第四步:載入模組
try {
module.load(filename);
hadException = false;
} finally {
if (hadException) {
delete Module._cache[filename];
}
}
// 第五步:輸出模組的exports屬性
return module.exports;
};複製程式碼
注意第三步是先存入快取,後載入模組,也就是說,模組只載入一次,後面的全用快取的了,所以之前的a.js載入過一次之後,剩下的都是用快取裡的new Module(filename, parent)了
這個new Module是一個物件,裡面包括exports屬性,這個exports屬性就是每個模組匯出的屬性,例如exports.done = false
本文結束