摘要:昨天我寫到所有函數都是閉包,有些同學表示還是接受不能。換句話說,它正是函數和聲明該函數的詞法環境的組合。原因詳見我的上一篇文章從過程式到函數式。我的相關文章閉包從過程式到函數式以上所有代碼按授權。
昨天我寫到“所有Javascript函數都是閉包”,有些同學表示還是接受不能。我好好的一個函數,怎么就成閉包了?那么,讓我們來探究一下,Chrome(V8)到底是怎樣實現閉包的。
從閉包到[[Scopes]]現在按下F12,打開console,讓我們隨便找一個實驗對象:
function simpleFunc() { } // <- undefined
超簡單超正常的函數吧,我們來驗證一下:
simpleFunc // <- ? simpleFunc() { }
說了超正常的,哪里閉包了?現在試試這個:
console.dir(simpleFunc) // ? simpleFunc() // arguments: null // caller: null // length: 0 // name: "simpleFunc" // prototype: {constructor: ?} // __proto__: ? () // [[FunctionLocation]]: VM000:1 // [[Scopes]]: Scopes[1]
咦,[[Scopes]]是什么?打開一看:
// [[Scopes]]: Scopes[1] // 0: Global?{type: "global", name: "", object: Window}
這就是閉包的實現。東西都存在這里了。看起來simpleFunc只不過是純潔的函數,但它實際上是(空的)自身代碼+全局變量環境。換句話說,它正是“函數和聲明該函數的詞法環境的組合”。
再來個稍微復雜點的例子:
{ let localVar = 1; function dirtyFunc() { return localVar++ } } // <- ? dirtyFunc() { return localVar++ }
console.dir(dirtyFunc) // ? dirtyFunc() // [[Scopes]]: Scopes[2] // 0: Block // localVar: 1 // 1: Global {type: "global", name: "", object: Window}
看,localVar存在這里了吧!大家老說什么“保持運行的數據狀態”云云,其實都在[[Scopes]]里。dirtyFunc看起來是個普通的函數,但[[Scopes]]里卻混了些東西。
所以,如果我們說人話,閉包實際上就是——
函數的代碼+[[Scopes]]
超級好理解了吧。
寧愿用this也不用閉包接下來讓我們對閉包做些更深入的解析,然后就知道為什么大家寧愿用this也不用閉包了。
[[Scopes]]能用代碼訪問/復制/修改嗎?不能。想不靠console,找到副作用在哪兒?不行。想深拷貝目前狀態?不行。想歷史回放?不行。debug?自己慢慢琢磨去吧!
閉包會把所有東西都存下來嗎?{ let localVar = 1; let unusedVar = 2; function dirtyFunc2() { return localVar++ } } console.dir(dirtyFunc2) // ? dirtyFunc() // [[Scopes]]: Scopes[2] // 0: Block // localVar: 1 // 1: Global {type: "global", name: "", object: Window}
至少Chrome是不會把所有東西都塞到閉包里的。
那閉包對垃圾回收沒害處?{ let localVar = new Uint8Array(1000000000) function dirtyFunc3() { return localVar } function cleanFunc() { } } var dirtyFunc3 = null console.dir(cleanFunc) // ? cleanFunc() // [[Scopes]]: Scopes[2] // 0: Block // localVar: Uint8Array(1000000000) [0, 0, …] // 1: Global {type: "global", name: "", object: Window}
dirtyFunc3和cleanFunc共享同一個[[Scopes]]項,但這個[[Scopes]]項并不會因為dirtyFunc3被回收而動態更新!所以無辜的cleanFunc就只好一直帶著這1GB的垃圾,內存泄漏妥妥的。作為強迫癥,這是我討厭閉包最重要的原因。
真的所有Javascript函數都是閉包嗎?console.dir(alert) // ? dirtyFunc() // [[Scopes]]: Scopes[0] // No properties
抱歉,我可能是不太嚴謹。很明顯,瀏覽器自帶的原生API函數都是在【里世界】聲明的,所以沒有詞法環境,自然[[Scopes]]是空的。它們不是閉包。
最佳實踐寧愿用this也不用閉包。原因詳見我的上一篇文章(從過程式到函數式)。
我的相關文章Javascript閉包:從過程式到函數式
以上所有代碼按Mozilla Public License, v. 2.0授權。
以上所有文字內容按CC BY-NC-ND 4.0授權。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/95497.html
摘要:想方設法糅合過程式與函數式兩種風格,忽略了閉包的基本假設,于是造出天坑。分散在各個閉包中的狀態會成為的溫床。當然,嚴格來說箭頭函數也是一種閉包,因為它是函數和詞法的組合。 編程語言的究極問題:過程式還是函數式? 閉包是函數式編程最先引進的,基本假設就是所有量都是常量。Javascript想方設法糅合過程式與函數式兩種風格,忽略了閉包的基本假設,于是造出天坑。 是什么 閉包的定義是函數和...
摘要:所以,有另一種說法認為閉包是由函數和與其相關的引用環境組合而成的實體。所以本文中將以維基百科中的定義為準即在計算機科學中,閉包,又稱詞法閉包或函數閉包,是引用了自由變量的函數。 閉包(closure)是JavaScript中一個神秘的概念,許多人都對它難以理解,我也一直處于似懂非懂的狀態,前幾天深入了解了一下執行環境以及作用域鏈,可戳查看詳情,而閉包與作用域及作用域鏈的關系密不可分,所...
摘要:在一個閉包環境內修改變量值,不會影響另一個閉包中的變量。直到看到函數閉包閉包這篇文章的代碼一部分,終于明白其中的邏輯了。 閉包 閉包定義:指擁有多個變量和綁定了這些變量的環境的表達式(通常是一個函數),因而這些變量也是該表達式的一部分。函數內部可以直接讀取全局變量。函數內部變量無法在函數外部訪問。函數內部聲明要用var或者let聲明,不然會變成全局變量鏈式作用域:子對象會一級級向上尋找...
摘要:項目地址閉包在計算機科學中,閉包英語,又稱詞法閉包或函數閉包,是引用了自由變量的函數。這個被引用的自由變量將和這個函數一同存在,即使已經離開了創造它的環境也不例外。 項目地址:https://git.io/pytips 閉包(Closure) 在計算機科學中,閉包(英語:Closure),又稱詞法閉包(Lexical Closure)或函數閉包(function closures),是...
摘要:全局環境是作用域鏈的終點。環境記錄類型定義了兩種環境記錄類型聲明式環境記錄和對象環境記錄聲明式環境記錄聲明式環境記錄是用來處理函數作用域中出現的變量,函數,形參等。變量環境變量環境就是存儲上下文中的變量和函數的。解析的過程如下 原文 ECMA-262-5 in detail. Chapter 3.2. Lexical environments: ECMAScript implement...
閱讀 2953·2023-04-26 01:32
閱讀 1548·2021-09-13 10:37
閱讀 2286·2019-08-30 15:56
閱讀 1678·2019-08-30 14:00
閱讀 3052·2019-08-30 12:44
閱讀 1969·2019-08-26 12:20
閱讀 1068·2019-08-23 16:29
閱讀 3233·2019-08-23 14:44