摘要:總結(jié)閉包的核心是屬性,在函數(shù)解析過程中,如果函數(shù)引用了外層函數(shù)的變量,那么外層函數(shù)即使自身被銷毀的活動(dòng)對象帶著對應(yīng)變量將會被保留,并且記錄在屬性中,作為作用域鏈的第二層,如果還引用了外層函數(shù)的外層函數(shù)的變量,那么對應(yīng)的活動(dòng)對象與變量也會被保
總結(jié):閉包的核心是[[scope]]屬性,在函數(shù)解析過程中,如果函數(shù)引用了外層函數(shù)的變量,那么外層函數(shù)(即使自身被銷毀)的活動(dòng)對象帶著對應(yīng)變量將會被保留,并且記錄在scope屬性中,作為作用域鏈的第二層,如果還引用了外層函數(shù)的外層函數(shù)的變量,那么對應(yīng)的活動(dòng)對象與變量也會被保留,并記錄,將會作為作用域鏈的第三層,依次類推...。當(dāng)函數(shù)被調(diào)用時(shí),所取到的外部變量的值將會是調(diào)用時(shí)各個(gè)變量的值。即當(dāng)前值。閉包調(diào)用時(shí)也是普通函數(shù),只不過作用域鏈多了閉包Closure的成分
閉包的用途:1.模仿塊級作用域,在立即調(diào)用函數(shù)中聲明內(nèi)部需要使用的變量;2.管理私有變量和方法;3.函數(shù)柯里化
來個(gè)極端的例子
function closure() { let result = [], count = 1 setInterval(() => {count++}, 1000) function outer() { let doufu = "wu" return function inner() { let foo = "foo" return function closureCallback() { let bar = {bar: "bar"}, bar1 = "bar" result[i] = function () { console.log(count) let b = doufu return bar.bar + i } } } } let inner = outer() let closureCallback = inner() for (var i=0; i<10; i++) { // [[scope]]包含closure{result, i} // 函數(shù)在這里立即調(diào)用 // i為實(shí)時(shí)值0,1,2,3... closureCallback() } return result }
從圖中可以看到,最終返回結(jié)果分別引用了closureCallback, outer,closure中的變量,所以,在作用域鏈中會保存這三個(gè)函數(shù)的活動(dòng)對象,不同時(shí)間調(diào)用,返回的count值不同,說明引用的是當(dāng)前值。
以下是手工示意圖
以下是我的學(xué)習(xí)過程
JavaScript里面的閉包,指的是函數(shù)與聲明該函數(shù)的詞法環(huán)境的組合。
一般在函數(shù)里面聲明函數(shù),并且引用外面函數(shù)的變量,就會產(chǎn)生閉包。定義在全局的時(shí)候,默認(rèn)不產(chǎn)生閉包(所謂閉包,就是當(dāng)內(nèi)部函數(shù)引用了外部函數(shù)中的變量時(shí),會在函數(shù)的作用域上面添加一層,這一層包含了函數(shù)所引用的外部函數(shù)的變量,存放在scope屬性里面,在調(diào)用時(shí),用于形成作用域鏈)
函數(shù)在執(zhí)行時(shí),會在內(nèi)存中創(chuàng)建一個(gè)活動(dòng)對象,該活動(dòng)對象包含arguments以及一些參數(shù)。并通過復(fù)制[[scope]]屬性中的對象構(gòu)建起執(zhí)行環(huán)境的作用域鏈。
var bar = "zoo" function Foo() { this.bar = bar }
function foo() { let bar = "zoo" function zoo() { let zoo = bar } } foo()
function closure() { let result = [] for (var i=0; i<10; i++) { result[i] = function () { return i } } return result } let arr = closure()
主要體現(xiàn)在函數(shù)返回函數(shù),函數(shù)A在調(diào)用函數(shù)B時(shí)被創(chuàng)建并從B函數(shù)的內(nèi)部返回。當(dāng)我們調(diào)用A函數(shù)的時(shí)候,B函數(shù)的作用域鏈已經(jīng)從內(nèi)存中銷毀,但是我們?nèi)匀豢梢栽贏中訪問B中存在的變量。因?yàn)锽中的變量仍然保存在A的活動(dòng)對象中(作用域鏈中[[scope]]對象里面)
此時(shí),函數(shù)A與A的scope構(gòu)成closure函數(shù)的閉包實(shí)例
從圖中可以看到,ar[0](以下稱為函數(shù)A)函數(shù)的作用域鏈最頂層為自身活動(dòng)對象(arguments, caller, length, name等等構(gòu)成)再往上則是由一個(gè)Closure對象實(shí)例構(gòu)成,可以看到這一層里面只包含一個(gè)變量i,即創(chuàng)建A時(shí)外層函數(shù)里面聲明的變量i。當(dāng)我們調(diào)用A時(shí),我們在第二層作用域鏈上面找到的這個(gè)i變量。
為什么arr數(shù)組每一項(xiàng)都返回的是10,而不是對應(yīng)的下標(biāo)值的原因就在這里:當(dāng)我們調(diào)用數(shù)組項(xiàng)函數(shù)時(shí),遇到變量i,并且在第二層作用域鏈讀取到i,這里面保存的i就是closure函數(shù)里面定義的i。在A調(diào)用時(shí),closure已經(jīng)執(zhí)完畢,在closure執(zhí)行的過程中i的值從0變到了10。這個(gè)性質(zhì)類似于把原本存在于closure函數(shù)中的變量,在closure函數(shù)執(zhí)行完畢后(從內(nèi)存中移除)我們可以在A自身的scope屬性里訪問到。
簡單一點(diǎn)說就是我們在調(diào)用A函數(shù)的時(shí)候,訪問到的i變量,是函數(shù)closure(雖然它不在了但是它的變量還在,仍然被scope屬性引用。)的當(dāng)前值(實(shí)時(shí)值)
所以要達(dá)到預(yù)期目標(biāo),我們只需要保證它的scope對象中保存的這個(gè)‘i’值是正確的就可以了。
這里面的思路就是,函數(shù)A被當(dāng)做普通函數(shù)調(diào)用時(shí),非閉包情況下,作用域就是自身(沒有i變量)+ 全局作用域(也沒有),所以這里還是需要借助閉包。即需要保證A上一層作用域的i是正確的值
1.創(chuàng)建另外一個(gè)閉包,每個(gè)i的值都會創(chuàng)建一個(gè)閉包
function closureCallback(i) { // 返回函數(shù)里面的i變量就是closureCallback函數(shù)的參數(shù)i return function() { return i } } function closure() { let result = [], b = "closure" for (var i=0; i<10; i++) { // 這里的closureCallback作為普通函數(shù)調(diào)用 // 且沒有引用closure函數(shù)的變量, // 函數(shù)作用域內(nèi)的變量,無法直接在函數(shù)外取得 // 所以作用域鏈不包含closure // 所以在closureCallback函數(shù)[[scope]]中不會有closure函數(shù) result[i] = closureCallback(i) } return result }
2.使用匿名閉包
function closure() { let result = [], b = "closure" for (var i=0; i<10; i++) { result[i] = (function (i) { return () => i })(i) } return result } let arr = closure()
3.使用let,減少閉包,let具有塊級作用域的效果
function closure() { let result = [], b = "closure" for (var i=0; i<10; i++) { let j = i result[i] = function () { return j } } return result } let arr = closure()
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/92836.html
摘要:當(dāng)初看這個(gè)解釋有點(diǎn)懵逼,理解成閉包就是函數(shù)中的函數(shù)了。里的閉包最近不滿足于只干前端的活,開始用起了。里的閉包最近在學(xué)習(xí)語言,讓我們來看一下語言里的閉包。在中,閉包特指將函數(shù)作為值返回的情況,被返回的函數(shù)引用了生成它的母函數(shù)中的變量。 本人開始接觸編程是從js開始的,當(dāng)時(shí)網(wǎng)上很多人說閉包是難點(diǎn),各種地方對閉包的解釋也是千奇百怪。如今開始接觸js以外的各種編程語言,發(fā)現(xiàn)不光是js,php、...
摘要:當(dāng)初看這個(gè)解釋有點(diǎn)懵逼,理解成閉包就是函數(shù)中的函數(shù)了。里的閉包最近不滿足于只干前端的活,開始用起了。里的閉包最近在學(xué)習(xí)語言,讓我們來看一下語言里的閉包。在中,閉包特指將函數(shù)作為值返回的情況,被返回的函數(shù)引用了生成它的母函數(shù)中的變量。 本人開始接觸編程是從js開始的,當(dāng)時(shí)網(wǎng)上很多人說閉包是難點(diǎn),各種地方對閉包的解釋也是千奇百怪。如今開始接觸js以外的各種編程語言,發(fā)現(xiàn)不光是js,php、...
摘要:一般來講,函數(shù)執(zhí)行完畢后,局部活動(dòng)對象就會被銷毀,內(nèi)存中僅保存全局作用域,但是閉包的情況有所不同理解閉包的前提先理解另外兩個(gè)內(nèi)容作用域鏈垃圾回收作用域鏈當(dāng)代碼在執(zhí)行過程中,會創(chuàng)建變量對象的一個(gè)作用域鏈。 閉包是javascript語言的一個(gè)難點(diǎn),也是它的特色,很多高級應(yīng)用都要依靠閉包來實(shí)現(xiàn)。個(gè)人的理解是:函數(shù)中嵌套函數(shù)。 閉包的定義及其優(yōu)缺點(diǎn) 閉包是指有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的...
摘要:如何在初學(xué)就理解閉包你需要接著讀下去。這樣定義閉包是函數(shù)和聲明該函數(shù)的詞法環(huán)境的組合。小結(jié)閉包在中隨處可見。閉包是中的精華部分,理解它需要具備一定的作用域執(zhí)行棧的知識。 這是本系列的第 4 篇文章。 作為 JS 初學(xué)者,第一次接觸閉包的概念是因?yàn)閷懗隽祟愃葡旅娴拇a: for (var i = 0; i < helpText.length; i++) { var item = he...
摘要:閉包引起的內(nèi)存泄漏總結(jié)從理論的角度將由于作用域鏈的特性中所有函數(shù)都是閉包但是從應(yīng)用的角度來說只有當(dāng)函數(shù)以返回值返回或者當(dāng)函數(shù)以參數(shù)形式使用或者當(dāng)函數(shù)中自由變量在函數(shù)外被引用時(shí)才能成為明確意義上的閉包。 文章同步到github js的閉包概念幾乎是任何面試官都會問的問題,最近把閉包這塊的概念梳理了一下,記錄成以下文章。 什么是閉包 我先列出一些官方及經(jīng)典書籍等書中給出的概念,這些概念雖然...
摘要:變量的作用域無非就是兩種全局變量和局部變量。其中內(nèi)部函數(shù)中可以訪問外部函數(shù)的變量,是因?yàn)閮?nèi)部函數(shù)的作用域鏈中包含了外部函數(shù)的作用域也可以理解為內(nèi)部函數(shù)的作用范圍輻射到了外部函數(shù)的作用范圍另一方面,在函數(shù)外部自然無法讀取函數(shù)內(nèi)的局部變量。 以前學(xué)習(xí)的時(shí)候,了解過變量提升和閉包,但是沒有深入了解,網(wǎng)上查了資料,這里記錄下,只供參考。部分內(nèi)容引用: https://www.cnblogs.c...
閱讀 1896·2021-11-11 16:55
閱讀 2105·2021-10-08 10:13
閱讀 755·2019-08-30 11:01
閱讀 2166·2019-08-29 13:19
閱讀 3293·2019-08-28 18:18
閱讀 2631·2019-08-26 13:26
閱讀 588·2019-08-26 11:40
閱讀 1879·2019-08-23 17:17