摘要:局部變量,當(dāng)定義該變量的函數(shù)調(diào)用結(jié)束時(shí),該變量就會(huì)被垃圾回收機(jī)制回收而銷毀。如果在函數(shù)中不使用匿名函數(shù)創(chuàng)建閉包,而是通過引用一個(gè)外部函數(shù),也不會(huì)出現(xiàn)循環(huán)引用的問題。
閉包是什么
在 JavaScript 中,閉包是一個(gè)讓人很難弄懂的概念。ECMAScript 中給閉包的定義是:閉包,指的是詞法表示包括不被計(jì)算的變量的函數(shù),也就是說,函數(shù)可以使用函數(shù)之外定義的變量。
是不是看完這個(gè)定義感覺更加懵逼了?別急,我們來分析一下。
閉包是一個(gè)函數(shù)
閉包可以使用在它外面定義的變量
閉包存在定義該變量的作用域中
好像有點(diǎn)清晰了,但是使用在它外面定義的變量是什么意思,我們先來看看變量作用域。
變量作用域變量可分為全局變量和局部變量。全局變量的作用域就是全局性的,在 js 的任何地方都可以使用全局變量。在函數(shù)中使用 var 關(guān)鍵字聲明變量,這時(shí)的變量即是局部變量,它的作用域只在聲明該變量的函數(shù)內(nèi),在函數(shù)外面是訪問不到該變量的。
var func = function(){ var a = "linxin"; console.log(a); // linxin } func(); console.log(a); // Uncaught ReferenceError: a is not defined
作用域相對(duì)比較簡單,我們不多講,來看看跟閉包關(guān)系比較大的變量生存周期。
變量生存周期全局變量,生命周期是永久的。局部變量,當(dāng)定義該變量的函數(shù)調(diào)用結(jié)束時(shí),該變量就會(huì)被垃圾回收機(jī)制回收而銷毀。再次調(diào)用該函數(shù)時(shí)又會(huì)重新定義了一個(gè)新變量。
var func = function(){ var a = "linxin"; console.log(a); } func();
a 為局部變量,在 func 調(diào)用完之后,a 就會(huì)被銷毀了。
var func = function(){ var a = "linxin"; var func1 = function(){ a += " a"; console.log(a); } return func1; } var func2 = func(); func2(); // linxin a func2(); // linxin a a func2(); // linxin a a a
可以看出,在第一次調(diào)用完 func2 之后,func 中的變量 a 變成 "linxin a",而沒有被銷毀。因?yàn)榇藭r(shí) func1 形成了一個(gè)閉包,導(dǎo)致了 a 的生命周期延續(xù)了。
這下子閉包就比較明朗了。
閉包是一個(gè)函數(shù),比如上面的 func1 函數(shù)
閉包使用其他函數(shù)定義的變量,使其不被銷毀。比如上面 func1 調(diào)用了變量 a
閉包存在定義該變量的作用域中,變量 a 存在 func 的作用域中,那么 func1 也必然存在這個(gè)作用域中。
現(xiàn)在可以說,滿足這三個(gè)條件的就是閉包了。
下面我們通過一個(gè)簡單而又經(jīng)典的例子來進(jìn)一步熟悉閉包。
for (var i = 0; i < 4; i++) { setTimeout(function () { console.log(i) }, 0) }
我們可能會(huì)簡單的以為控制臺(tái)會(huì)打印出 0 1 2 3,可事實(shí)卻打印出了 4 4 4 4,這又是為什么呢?我們發(fā)現(xiàn),setTimeout 函數(shù)時(shí)異步的,等到函數(shù)執(zhí)行時(shí),for循環(huán)已經(jīng)結(jié)束了,此時(shí)的 i 的值為 4,所以 function() { console.log(i) } 去找變量 i,只能拿到 4。
我們想起上一個(gè)例子中,閉包使 a 變量的值被保存起來了,那么這里我們也可以用閉包把 0 1 2 3 保存起來。
for (var i = 0; i < 4; i++) { (function (i) { setTimeout(function () { console.log(i) }, 0) })(i) }
當(dāng) i=0 時(shí),把 0 作為參數(shù)傳進(jìn)匿名函數(shù)中,此時(shí) function(i){} 此匿名函數(shù)中的 i 的值為 0,等到 setTimeout 執(zhí)行時(shí)順著外層去找 i,這時(shí)就能拿到 0。如此循環(huán),就能拿到想要的 0 1 2 3。
內(nèi)存管理在閉包中調(diào)用局部變量,會(huì)導(dǎo)致這個(gè)局部變量無法及時(shí)被銷毀,相當(dāng)于全局變量一樣會(huì)一直占用著內(nèi)存。如果需要回收這些變量占用的內(nèi)存,可以手動(dòng)將變量設(shè)置為null。
然而在使用閉包的過程中,比較容易形成 JavaScript 對(duì)象和 DOM 對(duì)象的循環(huán)引用,就有可能造成內(nèi)存泄露。這是因?yàn)闉g覽器的垃圾回收機(jī)制中,如果兩個(gè)對(duì)象之間形成了循環(huán)引用,那么它們都無法被回收。
function func() { var test = document.getElementById("test"); test.onclick = function () { console.log("hello world"); } }
在上面例子中,func 函數(shù)中用匿名函數(shù)創(chuàng)建了一個(gè)閉包。變量 test 是 JavaScript 對(duì)象,引用了 id 為 test 的 DOM 對(duì)象,DOM 對(duì)象的 onclick 屬性又引用了閉包,而閉包又可以調(diào)用 test ,因而形成了循環(huán)引用,導(dǎo)致兩個(gè)對(duì)象都無法被回收。要解決這個(gè)問題,只需要把循環(huán)引用中的變量設(shè)為 null 即可。
function func() { var test = document.getElementById("test"); test.onclick = function () { console.log("hello world"); } test = null; }
如果在 func 函數(shù)中不使用匿名函數(shù)創(chuàng)建閉包,而是通過引用一個(gè)外部函數(shù),也不會(huì)出現(xiàn)循環(huán)引用的問題。
function func() { var test = document.getElementById("test"); test.onclick = funcTest; } function funcTest(){ console.log("hello world"); }更多文章:lin-xin/blog
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/82150.html
摘要:再看一段代碼這樣就清晰地展示了閉包的詞法作用域能訪問的作用域?qū)?dāng)做一個(gè)值返回執(zhí)行后,將的引用賦值給執(zhí)行,輸出了變量我們知道通過引用的關(guān)系,就是函數(shù)本身。 在正式學(xué)習(xí)閉包之前,請(qǐng)各位同學(xué)一定要確保自己對(duì)詞法作用域已經(jīng)非常的熟悉了,如果對(duì)詞法作用域還不夠熟悉的話,可以先看: 深入理解閉包之前置知識(shí)---作用域與詞法作用域 前言 現(xiàn)在去面試前端開發(fā)的崗位,如果你的面試官也是個(gè)前端,并且不是太...
摘要:但閉包的情況不同嵌套函數(shù)的閉包執(zhí)行后,,然后還在被回收閉包會(huì)使變量始終保存在內(nèi)存中,如果不當(dāng)使用會(huì)增大內(nèi)存消耗。每個(gè)函數(shù),不論多深,都可以認(rèn)為是全局的子作用域,可以理解為閉包。 閉包(closure)是Javascript語言的一個(gè)難點(diǎn),也是它的特色,很多高級(jí)應(yīng)用都要依靠閉包實(shí)現(xiàn)。 閉包的特性 閉包有三個(gè)特性: 1.函數(shù)嵌套函數(shù) 2.函數(shù)內(nèi)部可以引用外部的參數(shù)和變量 3.參數(shù)和變量不會(huì)...
摘要:一般函數(shù)執(zhí)行完畢,局部活動(dòng)對(duì)象就會(huì)被銷毀,內(nèi)存中僅僅保存全局作用域,但是閉包會(huì)長期駐扎在內(nèi)存。我只是想通過這兩個(gè)例子來說明閉包的用處和好處。閉包會(huì)使變量始終保存在內(nèi)存中,如果使用不當(dāng)會(huì)增大內(nèi)存消耗。 閉包特性 函數(shù)嵌套函數(shù) 函數(shù)內(nèi)部可以引用外部的參數(shù)和變量 參數(shù)和變量不會(huì)被垃圾回收機(jī)制回收 閉包的作用 具體作用是有權(quán)訪問函數(shù)內(nèi)部的變量,最常見的就是函數(shù)內(nèi)部創(chuàng)建另一個(gè)函數(shù),通過另一個(gè)函數(shù)...
摘要:環(huán)境由閉包創(chuàng)建時(shí)在作用域中的任何局部變量組成。嚴(yán)格來說,閉包需要滿足三個(gè)條件訪問所在作用域函數(shù)嵌套在所在作用域外被調(diào)用閉包的形成原理先了解的垃圾回收機(jī)制會(huì)找出不再使用的變量,不再使用意味著這個(gè)變量生命周期的結(jié)束。 什么是閉包 最原始定義 閉包(closure),是指函數(shù)變量可以保存在函數(shù)作用域內(nèi),因此看起來是函數(shù)將變量包裹了起來。 //根據(jù)定義,包含變量的函數(shù)就是閉包 function...
摘要:理解的函數(shù)基礎(chǔ)要搞好深入淺出原型使用原型模型,雖然這經(jīng)常被當(dāng)作缺點(diǎn)提及,但是只要善于運(yùn)用,其實(shí)基于原型的繼承模型比傳統(tǒng)的類繼承還要強(qiáng)大。中文指南基本操作指南二繼續(xù)熟悉的幾對(duì)方法,包括,,。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。 怎樣使用 this 因?yàn)楸救藢儆趥吻岸?,因此文中只看懂?8 成左右,希望能夠給大家?guī)韼椭?...(據(jù)說是阿里的前端妹子寫的) this 的值到底...
閱讀 2575·2021-11-22 09:34
閱讀 3552·2021-11-15 11:37
閱讀 2355·2021-09-13 10:37
閱讀 2116·2021-09-04 16:40
閱讀 1596·2021-09-02 15:40
閱讀 2467·2019-08-30 13:14
閱讀 3337·2019-08-29 13:42
閱讀 1914·2019-08-29 13:02