摘要:閉包在我的前端學(xué)習(xí)中一直也是盲點(diǎn),之前很多次看到別人提到我都是完全聽不懂。閉包導(dǎo)致的問題因?yàn)殚]包會(huì)使得函數(shù)中的變量都保存在內(nèi)存中,如不能及時(shí)釋放會(huì)對(duì)性能造成影響。
閉包在我的前端學(xué)習(xí)中一直也是盲點(diǎn),之前很多次看到別人提到我都是完全聽不懂。最近一直看書和寫demo,對(duì)閉包也逐漸有所理解了,在這里寫下這篇博客。
從作用域鏈講起首先明確幾個(gè)概念:
1.JavaScript有函數(shù)級(jí)作用域,但沒有塊級(jí)作用域。
2.當(dāng)要使用一個(gè)變量時(shí),會(huì)沿著作用域鏈一步一步向上查找。
這里有一個(gè)demo:
var a = 1 function foo () { var a = 2 } foo() console.log(a) // a = 1
結(jié)果當(dāng)然是a = 1,雖然在foo函數(shù)中重新聲明了a并且賦給它一個(gè)新的值,但是var聲明的變量只在foo()函數(shù)中有效,函數(shù)執(zhí)行完畢就會(huì)銷毀,因此全局作用域中a的值沒有變化。
接下來再看這個(gè)demo:
for (var i = 0; i < 10; i++) { // code } console.log(i) // i = 10
這里在for循環(huán)結(jié)束之后仍然能在外部訪問到i,就是因?yàn)镴avaScript沒有塊級(jí)作用域造成的。
閉包的特性閉包的主要特性就是可以從外部訪問函數(shù)內(nèi)部的屬性和方法。先看一個(gè)demo:
function foo () { var a = 1 } foo() console.log(a) // 出錯(cuò)
正常情況下,定義在函數(shù)內(nèi)部的局部變量在函數(shù)執(zhí)行完之后就會(huì)被銷毀,因此在外部是無法訪問局部變量的。那應(yīng)該怎么做才能訪問呢?請(qǐng)繼續(xù)看:
function foo () { var a = 1 function bar () { console.log(a) } return bar } var baz = foo() baz() // 1
在這個(gè)demo中,我們?cè)诤瘮?shù)foo()內(nèi)部又定義了一個(gè)函數(shù)bar(),并把它的函數(shù)名返回,這樣便能在外部實(shí)現(xiàn)對(duì)內(nèi)部變量a的訪問。
有人問了:不是說函數(shù)執(zhí)行結(jié)束之后內(nèi)部變量會(huì)被銷毀嗎?你這不科學(xué)啊。
是的,原來函數(shù)執(zhí)行結(jié)束之后內(nèi)部變量的確會(huì)被銷毀,但是這里內(nèi)部函數(shù)bar()在foo()執(zhí)行時(shí)被返回并保存到了外部的baz中。這時(shí)候foo()執(zhí)行完后,baz中依舊保存著對(duì)函數(shù)bar()的引用,因此bar()的作用域并沒有被釋放,根據(jù)之前提到的變量查找方式,在bar()函數(shù)的外層作用域中找到了a。
以上就是使用閉包能從外部訪問內(nèi)部屬性的原理。
閉包的用途利用閉包強(qiáng)大的特性,最方便的用途就是實(shí)現(xiàn)私有變量和私有方法;另外,因?yàn)槭褂瞄]包會(huì)在內(nèi)存中保存函數(shù)作用域,因此也能保存變量的值。
此話怎解?請(qǐng)看下面的demo:
for (var i = 1; i <= 5; i++) { setTimeout(function timer () { console.log(i) }, i * 1000) } // 6 6 6 6 6
這里的結(jié)果并不是我們預(yù)想中的每隔一秒依次輸出12345,而是每隔一秒輸出一個(gè)6,為什么會(huì)這樣呢?
首先,這個(gè)6是變量i最終退出循環(huán)時(shí)的值,其次var聲明的變量沒有塊級(jí)作用域,因此i實(shí)際上在全局作用域中都訪問得到。到這里不難理解了,setTimeout在循環(huán)結(jié)束后執(zhí)行timer()函數(shù)時(shí),訪問的i實(shí)際上在全局作用域中,此時(shí)i=6,因此后面的每個(gè)timer()函數(shù)執(zhí)行訪問的i都是6。那么問題又來了,怎樣才能讓閉包訪問的i是我們想要的呢?
其實(shí)很簡(jiǎn)單,讓i變成局部變量,在循環(huán)結(jié)束之后銷毀,這樣每個(gè)閉包訪問的就是保存在作用域中的局部變量i,也就是對(duì)應(yīng)的12345。請(qǐng)看下面的demo:
for (var i = 1; i <= 5; i++) { (function (j) { setTimeout(function timer () { console.log(j) }, j * 1000) })(i) } // 1 2 3 4 5
這里用到了立即執(zhí)行函數(shù)(IIFE),可能有些難以理解。那我一步一步解釋:立即執(zhí)行函數(shù)其實(shí)就是JavaScript模仿塊級(jí)作用域的方法,這里你可以把它簡(jiǎn)單看成一個(gè)函數(shù)調(diào)用。i就是傳給調(diào)用函數(shù)的參數(shù),內(nèi)部匿名函數(shù)的形參j接收到i的值開始執(zhí)行setTimeout。此時(shí)j就是函數(shù)級(jí)作用域中的局部變量,在循環(huán)結(jié)束后,timer()函數(shù)開始執(zhí)行,因?yàn)樗且粋€(gè)閉包,所以能訪問保存在作用域中的變量j,也就輸出了我們想要的結(jié)果。
當(dāng)然更簡(jiǎn)單的方式是用ES6的let來聲明i,這樣也是使得i變成局部變量,其他關(guān)于ES6這里就不多提,有興趣的可以自行看let基本用法。
閉包導(dǎo)致的問題1.因?yàn)殚]包會(huì)使得函數(shù)中的變量都保存在內(nèi)存中,如不能及時(shí)釋放會(huì)對(duì)性能造成影響。
2.在IE9以下的瀏覽器會(huì)有內(nèi)存泄漏的問題。(關(guān)于這塊我后續(xù)會(huì)寫文章詳細(xì)說明)
本人經(jīng)驗(yàn)尚淺,目前對(duì)于前端仍在不斷摸索和學(xué)習(xí),文章如有錯(cuò)誤,歡迎各位指正。最后附上本人博客地址和原文鏈接,希望能向各位多多學(xué)習(xí)。
lbj的前端之路
原文鏈接:學(xué)習(xí)JavaScript之原型鏈
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/83125.html
摘要:接上回我寫了一篇關(guān)于閉包的博客學(xué)習(xí)之閉包,最后談到閉包導(dǎo)致的問題時(shí)留了一個(gè)尾在以下的瀏覽器中會(huì)有內(nèi)存泄漏的問題。今天的博客就繼續(xù)探索一下內(nèi)存泄漏的問題。博客地址的前端之路原文鏈接學(xué)習(xí)之內(nèi)存泄漏 接上回我寫了一篇關(guān)于閉包的博客《學(xué)習(xí)JavaScript之閉包》, 最后談到閉包導(dǎo)致的問題時(shí)留了一個(gè)尾: 在IE9以下的瀏覽器中會(huì)有內(nèi)存泄漏的問題。 今天的博客就繼續(xù)探索一下內(nèi)存泄漏的問題。 淺...
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。異步編程入門的全稱是前端經(jīng)典面試題從輸入到頁面加載發(fā)生了什么這是一篇開發(fā)的科普類文章,涉及到優(yōu)化等多個(gè)方面。 TypeScript 入門教程 從 JavaScript 程序員的角度總結(jié)思考,循序漸進(jìn)的理解 TypeScript。 網(wǎng)絡(luò)基礎(chǔ)知識(shí)之 HTTP 協(xié)議 詳細(xì)介紹 HTT...
摘要:在一個(gè)閉包環(huán)境內(nèi)修改變量值,不會(huì)影響另一個(gè)閉包中的變量。直到看到函數(shù)閉包閉包這篇文章的代碼一部分,終于明白其中的邏輯了。 閉包 閉包定義:指擁有多個(gè)變量和綁定了這些變量的環(huán)境的表達(dá)式(通常是一個(gè)函數(shù)),因而這些變量也是該表達(dá)式的一部分。函數(shù)內(nèi)部可以直接讀取全局變量。函數(shù)內(nèi)部變量無法在函數(shù)外部訪問。函數(shù)內(nèi)部聲明要用var或者let聲明,不然會(huì)變成全局變量鏈?zhǔn)阶饔糜颍鹤訉?duì)象會(huì)一級(jí)級(jí)向上尋找...
摘要:為了更好的理解,在閱讀此文之前建議先閱讀上一篇進(jìn)擊之詞法作用域與作用域鏈?zhǔn)裁词情]包閉包的含義就是閉合,包起來,簡(jiǎn)單的來說,就是一個(gè)具有封閉功能與包裹功能的結(jié)構(gòu)。在中函數(shù)構(gòu)成閉包。 為了更好的理解,在閱讀此文之前建議先閱讀上一篇《進(jìn)擊JavaScript之詞法作用域與作用域鏈》 1.什么是閉包 閉包的含義就是閉合,包起來,簡(jiǎn)單的來說,就是一個(gè)具有封閉功能與包裹功能的結(jié)構(gòu)。所謂的閉包就是...
摘要:今天同學(xué)去面試,做了兩道面試題全部做錯(cuò)了,發(fā)過來給道典型的面試題前端掘金在界中,開發(fā)人員的需求量一直居高不下。 排序算法 -- JavaScript 標(biāo)準(zhǔn)參考教程(alpha) - 前端 - 掘金來自《JavaScript 標(biāo)準(zhǔn)參考教程(alpha)》,by 阮一峰 目錄 冒泡排序 簡(jiǎn)介 算法實(shí)現(xiàn) 選擇排序 簡(jiǎn)介 算法實(shí)現(xiàn) ... 圖例詳解那道 setTimeout 與循環(huán)閉包的經(jīng)典面...
閱讀 1626·2021-11-24 09:39
閱讀 1182·2021-11-22 15:11
閱讀 2300·2021-11-19 11:35
閱讀 1685·2021-09-13 10:37
閱讀 2635·2021-09-03 10:47
閱讀 2238·2021-08-30 09:47
閱讀 1703·2021-08-20 09:39
閱讀 3009·2019-08-30 14:13