文章寫很多次,你現在應該也知道,JavaScript 被創造的目標是一個「自認為很聰明的語言」
,有很多「很聰明(自認)的語言才會有的特性」,所以可以說 JavaScript 是一種 consumer-oriented language。
很久以前就有 Closure 這個概念,但是以前的人不一定知道要怎麼用,
以歷史的角度來看,現代的語言都是有 Closure 特性的
,如此才能說一個程式語言是真正有威力的語言。
時間拉回 1995 年,Brendan Eich(JavaScript 主要創造者與架構師)加入 Netscape,
那個時候很多現代語言的特性都是存在於 academic language,比如 Closure 基本上只存在學術研究。
1995 年有 Closure 特性,最常見的目的之一是為了寫 functional programming language,但 Brendan Eich 想把它加進去 JavaScript。
順帶一提,1995 年有 Closure 這個特性,但不是研究用的程式語言,另一個是 Perl
,同時期幾乎其他語言都沒有,所以才會說 Closure 是 academic language 的東西。
很顯然的,1995 年那群工程師根本不會在意這種學院派的東西,更不可能會使用到 Closure 這種東西,他們需要一個商業用,實務上可以用的程式語言。
然後你可以從上帝視角看到,現今的 JavaScript 和 Closure 不可分割,那時候 Brendan Eich 某種程度上,為了隱含這個意圖,創造一堆大括號 {..} 和分號的語法,然後還命名叫做 JavaScript 試圖瞞混過去。
(你可以從 JavaScript 這個名字上,看出當時 Java 有多受歡迎,一個想蹭熱度流量的概念)
1995 年 JavaScript 的 大括號 {..} 真的沒啥意義,另外分號要不要使用現在已經是一種團隊默契了,但最開始的想法只是想做的和 C++ 或是目標 Java 程式撰寫風格做得很像
,藉此偷渡 JavaScript 其實就是 Closure 的意圖(直接說反而被拒絕,換個皮就成功拉 XDD)。
講了那麼多故事,Kyle 只是想說,JavaScript 活那麼久沒有被消滅,很大一部分是提前拿到 Closure 這個特殊能力。
如果你要找書上
或是 wiki
的 Closure 定義,那多半還是學術定義,大多數的時候並不能幫助你程式開發。
所以我們接下來會把目光放在 有 Clousre 的語言,會有什麼天性
,
但首先,你必須理解 lexical scope。
註:如果你好奇 JavaScript 歷史故事
dotJS 2017 - Brendan Eich - A Brief History of JavaScript
- 補充:之前寫過的 static scoping(靜態作用域)
[day20] YDKJS (Scope) : Advanced Scope
Lexical Scope : 可以預測的 Scope
Dynamic scope : 很彈性,隨時會改變的 Scope
Closure is when a function is able to
remember and access
its lexical scope, the variablesoutside of itself
, so-called free variables, when it's able to access that lexical scope, even when that function executes in a different scope.
大家看原文,我不敢翻譯怕有錯 XDD
簡體翻譯:
閉包就是函式能夠記住並訪問它的詞法作用域,即使當這個函式在它的詞法作用域之外執行時。
只是正常 lexical scope : 找不到的東西會往上層找,直到 global。
需要滿足另一個特性:
可以記得
並存取原本的 lexical scope
。
但現在可以被保留!!
現在被 Closure 保留住了。 如果有圖來說 :
這邊先不解釋
setTimeout 的運作機制,
你可以想像
JavaScrip 引擎委託 網頁瀏覽器
存放一這個 waitASec(){...} 並且設定時間
100 毫秒後,再排隊執行(要排隊等全部 JS 執行後
才能輪到 waitASec(){...} )。
參考資料
想像中,code會這樣跑:
全部消失
。這樣想你是對的
,但是少一個步驟
:
一個神奇的地方出現了,question
居然被保留著 !!
如果有找到就回傳 Magic Scope 的東西,
如果 Magic Scope 沒有找到,就像是正常 lexical scope 一樣繼續回去 Current Scope 往上層找,就會是原先的 is not defined
在 function被呼叫時
,會檢查有沒有其他人引用這個 scope 內部的值,如果有就會貼上一張[[Scope]]
的貼紙當作標籤。
在正常 lexical scope 往上層找,其實就是沿著 [[Scope]]
貼紙找 (a.k.a. 沿著 scope chain )。
然後 function執行完
,會撕掉一開始 identifier 的 [[Scope]]
貼紙。
很顯然,Closure 就是那一張亂貼的機制,被貼上[[Scope]]
,所以會優先去找到這個被保留著的 Magic Scope (Closure)。
要注意,Closure 保留下來的時候,只剩下
這張貼紙,其他貼紙都撕掉了。
其他貼紙都撕掉了,也意味著原本 function declaration 宣告的 identifer 不能存取到當時的 function 了。
這邊比較麻煩一點,可以透過 Spce. 來輔助說明,
Spce. 裡面針對有寫 Object 是 mutability,並且都是指向同一個 Object
.
用常用術語說就是 by reference,但是有貼到貼紙都可以碰到這個 reference ,所以這個 reference 是被
shared
, a.k.a. pass by shared
var a = { obj: 'someObj'};
var b = a;
a; // {obj: "someObj"}
b; //
這時候代表 {obj: "someObj"} 上面貼兩張 label ,分別叫做 a, b 。
我們這個系列一開始就說過 JavaScript 的
值
比較重要。
這邊補充:變數是我們比較方便存取的方法
然後,function
是一種 Object。
我們舉例的程式,不嚴謹的看大概是這種感覺:
ask
標籤會全部消失。有問題請幫我指證 QQ
其中,圈圈很大一圈,代表 closure 是 scope-based
,不是
以變數為單位。
原本 ask 應該會被撕掉,然後整個 function 被清空,
但是今天我把傳進去的參數保留在 myQuestion 這個 identifer 上:
line 7 : 其實就是貼一個標籤在 真實code
上面
如果你今天輸入 identifer,會看到是原本 ask function return 整個 function object(code)
myQuestion; // ƒ holdYourQuestion(){ console.log(question)}
還是一句話: function 也是物件。
所以你要執行 function
,後面要加上()
才會是執行 expression。
myQuestion(); // 執行 holdYourQuestion() : console.log(question)
但今天 clousre 保留整個黃色圈圈,
也就是把一開始傳入 ask 的 parameters、augment 也都保留著,
所以我們 console.log 會先找到 之前傳的字串myQuestion(); // console.log(question)
myQuestion(); // "What is closure?"
這個例子比較偏向 functional programming 的概念,把
函數回傳
另一個函數
。
順帶一提,這個部分因為 myQuestion
這個 identifer 貼在整個 scope 上面,
所以你可以重複使用
這整塊存好的 code,
並且也只有 myQuestion()
做 expression 可以執行它!
(因為其他人沒有這張貼紙,也不可能改到裡面的東西了。)
說到這邊,484 覺得這個隔離的感覺,有一種 private, 有一種內聚力(Cohesion)很高的味道?
下集待續 ...
註:明天來聊看看其他運用。
他們創造新語言的目的是要抗衡最強勢的語言 Java ,
這也是創造 JavaScript 的目標之一:成功吃掉 Java 或 消失。
這邊跟我的印象不太一樣
我印象中創造 Java 的 Sun,與創造 JavaScript 的 Netscape 反而是合作聯盟的關係
主要是對抗 MicroSoft 的樣子
當時瀏覽器還沒有能夠操作圖形界面的語言,只有 HTML
Netscape 內部因此想要有個語言,能操作畫面上的元素
本來有考慮用 Java,但好像性能的關係就沒採用
但因為要跟 Sun 聯盟,語法上跟 API 就借鑒了很多 Java 的東西
Netscape 瀏覽器也支援 Java 的插件
連名字也從 LiveScript 變成 Java 的形狀了
最後,Sun 在 1995 年將 Oak 改名成 Java
NetScape 也在同一年推出 JavaScript
印象中,當時的說明文件中還說 JavaScript 是 Java 的腳本語言 XD
這邊可能是我誤解 Kyle 說的 Orz
那我直接拿掉這一段好了,感謝資訊~