摘要:昨天我寫到所有函數(shù)都是閉包,有些同學(xué)表示還是接受不能。換句話說,它正是函數(shù)和聲明該函數(shù)的詞法環(huán)境的組合。原因詳見我的上一篇文章從過程式到函數(shù)式。我的相關(guān)文章閉包從過程式到函數(shù)式以上所有代碼按授權(quán)。
昨天我寫到“所有Javascript函數(shù)都是閉包”,有些同學(xué)表示還是接受不能。我好好的一個函數(shù),怎么就成閉包了?那么,讓我們來探究一下,Chrome(V8)到底是怎樣實(shí)現(xiàn)閉包的。
從閉包到[[Scopes]]現(xiàn)在按下F12,打開console,讓我們隨便找一個實(shí)驗(yàn)對象:
function simpleFunc() { } // <- undefined
超簡單超正常的函數(shù)吧,我們來驗(yàn)證一下:
simpleFunc // <- ? simpleFunc() { }
說了超正常的,哪里閉包了?現(xiàn)在試試這個:
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}
這就是閉包的實(shí)現(xiàn)。東西都存在這里了。看起來simpleFunc只不過是純潔的函數(shù),但它實(shí)際上是(空的)自身代碼+全局變量環(huán)境。換句話說,它正是“函數(shù)和聲明該函數(shù)的詞法環(huán)境的組合”。
再來個稍微復(fù)雜點(diǎn)的例子:
{ 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存在這里了吧!大家老說什么“保持運(yùn)行的數(shù)據(jù)狀態(tài)”云云,其實(shí)都在[[Scopes]]里。dirtyFunc看起來是個普通的函數(shù),但[[Scopes]]里卻混了些東西。
所以,如果我們說人話,閉包實(shí)際上就是——
函數(shù)的代碼+[[Scopes]]
超級好理解了吧。
寧愿用this也不用閉包接下來讓我們對閉包做些更深入的解析,然后就知道為什么大家寧愿用this也不用閉包了。
[[Scopes]]能用代碼訪問/復(fù)制/修改嗎?不能。想不靠console,找到副作用在哪兒?不行。想深拷貝目前狀態(tài)?不行。想歷史回放?不行。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]]項并不會因?yàn)?b>dirtyFunc3被回收而動態(tài)更新!所以無辜的cleanFunc就只好一直帶著這1GB的垃圾,內(nèi)存泄漏妥妥的。作為強(qiáng)迫癥,這是我討厭閉包最重要的原因。
真的所有Javascript函數(shù)都是閉包嗎?console.dir(alert) // ? dirtyFunc() // [[Scopes]]: Scopes[0] // No properties
抱歉,我可能是不太嚴(yán)謹(jǐn)。很明顯,瀏覽器自帶的原生API函數(shù)都是在【里世界】聲明的,所以沒有詞法環(huán)境,自然[[Scopes]]是空的。它們不是閉包。
最佳實(shí)踐寧愿用this也不用閉包。原因詳見我的上一篇文章(從過程式到函數(shù)式)。
我的相關(guān)文章Javascript閉包:從過程式到函數(shù)式
以上所有代碼按Mozilla Public License, v. 2.0授權(quán)。
以上所有文字內(nèi)容按CC BY-NC-ND 4.0授權(quán)。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/95497.html
摘要:想方設(shè)法糅合過程式與函數(shù)式兩種風(fēng)格,忽略了閉包的基本假設(shè),于是造出天坑。分散在各個閉包中的狀態(tài)會成為的溫床。當(dāng)然,嚴(yán)格來說箭頭函數(shù)也是一種閉包,因?yàn)樗呛瘮?shù)和詞法的組合。 編程語言的究極問題:過程式還是函數(shù)式? 閉包是函數(shù)式編程最先引進(jìn)的,基本假設(shè)就是所有量都是常量。Javascript想方設(shè)法糅合過程式與函數(shù)式兩種風(fēng)格,忽略了閉包的基本假設(shè),于是造出天坑。 是什么 閉包的定義是函數(shù)和...
摘要:所以,有另一種說法認(rèn)為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實(shí)體。所以本文中將以維基百科中的定義為準(zhǔn)即在計算機(jī)科學(xué)中,閉包,又稱詞法閉包或函數(shù)閉包,是引用了自由變量的函數(shù)。 閉包(closure)是JavaScript中一個神秘的概念,許多人都對它難以理解,我也一直處于似懂非懂的狀態(tài),前幾天深入了解了一下執(zhí)行環(huán)境以及作用域鏈,可戳查看詳情,而閉包與作用域及作用域鏈的關(guān)系密不可分,所...
摘要:在一個閉包環(huán)境內(nèi)修改變量值,不會影響另一個閉包中的變量。直到看到函數(shù)閉包閉包這篇文章的代碼一部分,終于明白其中的邏輯了。 閉包 閉包定義:指擁有多個變量和綁定了這些變量的環(huán)境的表達(dá)式(通常是一個函數(shù)),因而這些變量也是該表達(dá)式的一部分。函數(shù)內(nèi)部可以直接讀取全局變量。函數(shù)內(nèi)部變量無法在函數(shù)外部訪問。函數(shù)內(nèi)部聲明要用var或者let聲明,不然會變成全局變量鏈?zhǔn)阶饔糜颍鹤訉ο髸患壖壪蛏蠈ふ?..
摘要:項目地址閉包在計算機(jī)科學(xué)中,閉包英語,又稱詞法閉包或函數(shù)閉包,是引用了自由變量的函數(shù)。這個被引用的自由變量將和這個函數(shù)一同存在,即使已經(jīng)離開了創(chuàng)造它的環(huán)境也不例外。 項目地址:https://git.io/pytips 閉包(Closure) 在計算機(jī)科學(xué)中,閉包(英語:Closure),又稱詞法閉包(Lexical Closure)或函數(shù)閉包(function closures),是...
摘要:全局環(huán)境是作用域鏈的終點(diǎn)。環(huán)境記錄類型定義了兩種環(huán)境記錄類型聲明式環(huán)境記錄和對象環(huán)境記錄聲明式環(huán)境記錄聲明式環(huán)境記錄是用來處理函數(shù)作用域中出現(xiàn)的變量,函數(shù),形參等。變量環(huán)境變量環(huán)境就是存儲上下文中的變量和函數(shù)的。解析的過程如下 原文 ECMA-262-5 in detail. Chapter 3.2. Lexical environments: ECMAScript implement...
閱讀 2962·2023-04-26 01:32
閱讀 1557·2021-09-13 10:37
閱讀 2292·2019-08-30 15:56
閱讀 1686·2019-08-30 14:00
閱讀 3062·2019-08-30 12:44
閱讀 1976·2019-08-26 12:20
閱讀 1072·2019-08-23 16:29
閱讀 3238·2019-08-23 14:44