require()迴圈引用問題

lio-mengxiang發表於2019-03-06

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

本文結束


相關文章