為什麼我require的module是empty object !? Node.js 踩坑記「modules cycles」

布魯斯前端
3 min readNov 1, 2019

--

這是一個被一年前的自己坑的故事…

這是最近在重構自己一年前寫的code時,發現的一個技術債也蠻有趣的,所以就把它記錄下來,希望能幫助到跟我遇到一樣問題的人,解決你心中的WTF。

遇到的問題是什麼?

這篇文章我也不想寫的太長,所以就簡單敘述一下情境吧,當時的情境是這樣的…

重構到某個js檔案時,打上 const constants = require('./constants') 時發現,為什麼constants 變數是empty object {} !?我明明有做 module.exports 啊啊啊??

原因到底是什麼?

其實遇到這種問題有很多解決方法,比如像是直接在調用某個function時把constants傳進去就可以了,但找出源頭才是解決問題最好的方法,所以還是花了點時間在google searching上尋找答案,想瞭解到底是遇到啥鬼問題。

最後得到的答案是,Node本身對require的call有一個防呆的設計,就是只要developer寫出modules cycles的時候,就會自動幫你把還沒讀完的檔案export出一個「空」object。
也就是你有個 a.js require b.js,然後 b.js require a.js一直無限循環下去的感覺。

所以這就是為什麼會說這是技術債…完全就是一年前的自己寫的一份爛code然後坑了現在的自己…

什麼是modules cycles

想直接看原文的人可以直接參考官方的文件連結,以下的解釋都是直接修改原文的範例,官方解釋的其實就很清楚了,這裡只是做個解說而已。https://nodejs.org/api/modules.html#modules_cycles

先來一段官方對這種問題的簡述

When there are circular require() calls, a module might not have finished executing when it is returned.

這裡來舉例個簡單的例子,假設我今天a.js是長這樣

// a.jsconst b = require('b.js')... do something ...module.exports = { name: 'A' }

然後有個b.js

// b.jsconst a = require('a.js')... do something ...module.exports = { name: 'B' }

最後我有個main.js來呼叫a.js

// main.jsconst a = require('a.js')

執行順序是

  1. main 呼叫 a.js
  2. a.js 呼叫 b.js
  3. b.js 呼叫 a.js …..

理論上這應該要是無限循環,但就跟前面提到的一樣,Node本身有做一個防呆,當走到第三步時,Node會發現b.js呼叫了還沒執行完的a.js,因為a.js還沒執行完,所以就索性回傳一個空的object給b.js,防止你進入無限循環。

簡單來說就是當你在require的檔案還沒被執行完成時,那Node就會返回一個空的object給你。

大概就是這樣~每次遇見問題都是成長的好機會,而且解決問題的那種成就感也算是當工程師的一種樂趣吧。

希望這篇文章能幫助跟我一樣遇到這個問題的人~

參考文件來源:https://nodejs.org/api/modules.html#modules_cycles

--

--