在開發 React Component 或 Hook 時,常會看到像這樣的程式碼:
這看起來像是違反了 JavaScript 的規則,因為你在宣告 someStrategy 之前就用了它,但實際上這是合法的行為,也是 JavaScript 的預期表現。
一般來說,我們會在學習 JavaScript 的過程中看到這件事情:在 JavaScript 中,使用 const 或 let 宣告的變數不能在宣告之前被使用,否則會拋出 ReferenceError,這段期間稱為 TDZ(Temporal Dead Zone,暫時性死區)。
也就是說,如果你在變數「初始化之前」就嘗試使用它,JS 會拒絕執行:
所以我們直覺上會認為:
變數宣告在下面,上面就不該能用了吧?
但實際上,在 React 函式(或 Hook)中這種結構是合法的,讓我們一起看下去吧。
React 並沒有魔法
看起來在 React 中很神奇的事情,其實背後還是離不開 JavaScript 原本的行為。讓我們先來看看兩件事情的運作:Hoisting & 函式延遲執行(Deferred Execution)。
1. 模組層級的 Hoisting(提升)
在 ES Module 中,所有頂層的變數(const、let、var)、函式(function)在解析階段都會被「提升(hoist)」,意思是:JavaScript 會先掃描整個模組,把變數名稱、函式簽名都「先登記起來」。
不過 const/let 會處在 Temporal Dead Zone(暫時性死區,TDZ) 中,直到程式執行到那行才初始化。因此:
2. 函式是延遲執行(Deferred Execution)
在 JavaScript 中,函式的「定義」和「執行」是兩回事。
當 JS 解讀一段程式時,它會先記住你定義了哪些函式,但不會馬上執行它們,直到你主動「呼叫」它們為止。
舉個例子:
在 React 裡也是一樣,useHello 是一個函式,它的內容直到被呼叫時才會執行。因此即使 strategy 使用了尚未「寫到」的 helloStrategy,只要函式呼叫的時候 helloStrategy 已經初始化完,就不會有問題。
綜合以上兩點,我們可以理解到 React 並沒有魔法,在 const 定義前先呼叫並使用 const 的值是 JavaScript 裡正常的行為,只是因為 模組先執行、函式後呼叫 所帶來的結果。