全网最通透的“闭包”认知 · 跨越语言
閉包作為前端面試的必考題目,常讓1-3年工作經驗的JavaScripter感到困惑,其實主流語言都有閉包。
今天我們深入聊一聊[閉包], 查缺補漏!
?1.?以面試題 ·? 投石問路?2.?以C#閉包 ·? 庖丁解牛?3.? ? 跨越語言?·追本溯源? ??? 頭等函數? ??自由變量? ??詞法作用域4.??答面試題?· 返璞歸真
1. 投石問路
調用下面函數,輸出結果是什么樣呢?
static void Closure1() {for (int i = 0; i < 5; i++){ Task.Run(()=> Console.WriteLine(i));} } // 輸出: 5 5 5 5 5是不是很意外?如何輸出原本預期的 0,1,2,3,4。
bingo, 加一個臨時變量就可以解決。
static void Closure2() {for (int i = 0; i < 5; i++){int j = i;Task.Run(() => Console.WriteLine(j));} } // 輸出: 3 0 1 4 2 // 多次執行的結果不一樣,但是總是會保持輸出 0,1,2,3,4 的亂序組合以上閉包概念涉及到 Task任務,理解起來更加復雜,我們來看一個基礎的C#閉包。
2. 庖丁解牛
一個閉包就是一個“捕獲”了其生成的環境中、所引用的自由變量的函數。
這個被引用的自由變量將和這個函數一同存在,即使已經離開了創造它的環境也不例外。
我們首先定義了一個委托action,它引用了“x”變量(x變量既不是入參,也不是委托內的局部變量), 這個變量將被action"捕獲”,被自動添加到action 的運行環境。
當我們執行action時,原始的“x”已經脫離了它被引用時的作用域環境,但是兩次執行能輸出2,3 說明它脫離原引用環境仍然能用。
當你在代碼調試器(debugger)里觀察“action”時,可以看到C#編譯器為我們創建了一個Target屬性,里面封裝了 x 變量:
源碼追溯,委托繼承自Delegate抽象類,Delegate類有個Target?屬性(獲取當前委托調用實例方法的實例類)?。
至此可以猜想: 我們每次執行委托,實際是是執行某個匿名類上的實例方法。
都說了閉包是跨越語言的設計, 至少我知道 JavaScript C# Go都有閉包。
3. 追本溯源
閉包是詞法閉包的簡稱,維基百科上是這樣定義的:
“在計算機編程中,閉包是在詞法環境中綁定自由變量的頭等函數”。
頭等函數
頭等函數( First Class)意味著語言將其視為第一類數據類型的函數, 意味著你可以將函數分配給一個變量(或作為參數傳遞),然后像正常函數一樣調用。
很明顯,C#常使用的委托(C#委托的演進:匿名函數-->lambda表達式)是頭等函數。
Func<string,string> myFunc = delegate(string var1){return "some value"; }; Func<string,string> myFunc = var1 => "some value"; string myVar = myFunc("something");自由變量
自由變量是在匿名函數/lambda表達式中被引用的變量,它不是函數的參數也不是函數的局部變量。
var myVar = "this is good"; Func<string,string> myFunc = delegate(string var1){return var1 + myVar; };詞法作用域引用的自由變量,注意,是引用自由變量,并不是使用當時自由變量的值。
??通俗點, 就是告知這個變量環境,我這個匿名函數等會執行時要用到這個變量;如果我沒被銷毀,你不能銷毀我引用的自由變量。
我們再回過頭來看[投石問路]的面試題。
4. 返璞歸真
首先你要知道:循環內開啟的Task任務,并不保證執行順序。
Demo1:輸出5,5,5,5,5
這是因為在 for循環內,開啟了5個Task任務,每個任務均引用了自由變量i (相對于每個任務執行環境,i 屬于全局變量);
for循環先執行完,i=5, 5個任務輸出時自然得到值5。
為什么加上臨時變量就能輸出"預期"?
Demo2:輸出亂序的0,1,2,3,4
這是因為 在for循環內,每次循環j均拷貝自當時的i,每個任務均引用了自由變量 j (每個任務執行環境均維護了一個變量j);
任務亂序執行時依舊能獲取本任務綁定的自由變量j。
有這樣的認知,理解JavaScript 閉包也就不難了。
# 總結本文屏蔽語言差異,理清了[閉包]的概念核心:?頭等函數、自由變量,不僅能幫助我們應對多語種有關閉包的面試題, 也幫助我們了解[閉包]在通用語言中的設計初衷。
原創不易 點個在看支持下~
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的全网最通透的“闭包”认知 · 跨越语言的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 使用 Github Actions ar
 - 下一篇: 技术分享 | jaeger链路日志实现