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

Bruce
Bruce
Nov 1 · 3 min read

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

這是最近在重構自己一年前寫的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

Bruce

Written by

Bruce

Be a world class developer, Front-End engineer.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade