重新思考「全域變數」是什麼

*** 本文簡體版已發表於超理論壇: 重新思考什么是「全局变量」 - 超理论坛

幾乎所有人都同意,全域變數 (global variable) 是「不好」的,「應該避免」的,但是由於没有理解它的實質,一般只能夠避免字面意義的 global variable 而已,因此可能會寫出很爛的 code. 這篇文章會說明 global variable 的實質,幫大家寫出更好的 code.

What is global variable? Wikipedia 說 "a global variable is a variable with global scope, meaning that it is visible (hence accessible) throughout the program". 但是我並不認同。這個定義只看到了表象,没有看到實質。為什麼只是表象?很簡單,比如,假如有一個 C++ program 只有一個 class 而且是 singleton, 即使它没有字面意義的 global variable, 它的那個 singleton class 的 member variable 其實和 global variable 實質上也没有什麼區別。

class Singleton {
private:
    int member;  // This is equivalent to a global variable
// ......
};
int main() {
    auto instance = new Singleton;
    // ......
}

我們把一個東西叫做 global variable, 應該是基於實質上它是不是 global 的,而不是基于它在不在字面意義上的 global scope. 所謂 global 或 local, 都是相對的,不是絕對的。一個 local variable 或是 member variable, 諸如此類的東西,也可能在某個很大的 scope 裡面可以看成是一個 global variable. 基本上,只要是 widely accessible mutable value, 就可以認為是相對的 global variable, 但實際寫 code 的時候就没有人 care 了。

當然,空談道理是没用的。軟體工程的道理,只有拿出實際的例子才有說服力。

以我熟悉的 Web 領域為例。React 和 Vue 等等的 framework, 現在已經基本上取代了 vanilla JS. 這些 framework 的核心思想(之一)都是「組件化(元件化)」。每個 component 有自己的狀態,不需要 global variable 到處飛了,相比於 vanilla JS 是一個巨大的進步。消除了 global variable, 似乎「組件化」這種思想就是 Web 的最終解決方案了。一切看起來如此美好。

然而故事並没有完結。一聲驚雷,React Hooks 横空出世!!!

然後 Vue Composition API 也來了!!!

Ohhhhh! What's going on? 顯然,「組件化」並不是 Web 的最終解決方案。「組件化」其實是有很大局限性的。

一張有名的 GIF,可以很清楚地說明為什麼會有 Hooks 這項技術的出現:

可以看到,Hooks 把 component 裡面分散在不同位置的相關 code 各自集中了,每個 hook 只處理和它自己相關的狀態和副作用,並且多個 hook 是可以任意組合的。相比之下,原來的「組件化」方案 code 散亂,component 裡面的邏輯不容易拆分組合。

一言以蔽之,「組件化」只把 code 拆分到了 component 的級別,把 component 作為状態和副作用的最小單元,對於更細粒度的拆分自然無能為力。

不過,Hooks 和文本的主題 global variable 的關係是什麼?注意,在原本的「組件化」方案中,每個 component 都有一個自己的狀態 (state), 它是狀態的最小單元。如果一個 component 特別複雜,比如有 1000 行 code, 那麼在這個 component 裡面,這個 state 其實就相當於一個 global scope, 而 state 的內容就相當於是 global variable. 畢竟,所謂 global 或是 local 都是相對的。雖然 state 只是一個 member variable, 它裡面的內容只是一個 member object 的 property, 但當 component 足夠複雜時,這些 property 作為一些 widely accessible mutable value, 把它們看做是 global variable 才是更合適的。所以,「組件化」看上去好像是消除了 global variable, 但其實没有。

其實大家早就意識到單純的「組件化」是不夠用的,於是做出了 Redux/Vuex 等「中心化狀態管理」library. 這些 library 的思想其實就是,不管它,我就是要用 global variable! 全域變數最方便!不用 state 那種看上去像 local 的東西了!直接 global 了!不演了!!! 盡管它們解決了一些問題,但因為是治標不治本,最終 Hooks 還是來了。(所以不要覺得 Flux/Elm architecture 很厲害,其實它們只是在用 global variable 而已)

當 component 足夠複雜時, state 的內容其實可以看成是 global variable. 如果能提前意識到這一點,Web 領域就不會走這麼多彎路才搞出 Hooks 之類的技術。太可惜了。雖然有些「事後諸葛」,但我們可以「亡羊補牢」。心中有一個意識,不是只有形式上的 global variable 才是 global variable. 所謂 global 和 local 都是相對的。我希望我們將來不要再走相同的彎路。

This page intends for crawlers/spiders/bots of search engines. If you didn't modify your HTTP UserAgent, please report the bug to spider-detector.