摘要:常見問題說到閉包相關(guān)的問題,最典型的就是變量和指向這兩類問題。如果有錯(cuò)誤或不嚴(yán)謹(jǐn)?shù)牡胤?,歡迎批評指正,如果喜歡,歡迎點(diǎn)贊。
引言
閉包這個(gè)詞對很多前端開發(fā)人員來說既熟悉又陌生,熟悉是因?yàn)楹芏嗳硕加眠^閉包,但是用的時(shí)候不知道閉包,陌生是因?yàn)椴⒉焕斫忾]包,接下來這篇文章將會從多方面介紹閉包
定義閉包是怎么定義的呢?當(dāng)函數(shù)可以記住并訪問所在的詞法作用域時(shí),就產(chǎn)生了閉包,即使函數(shù)在當(dāng)前詞法作用域之外執(zhí)行。來看一個(gè)具體例子:
function foo () { var a = 2 function bar () { console.log(a) } return bar } var baz = foo() baz() //2
函數(shù)bar的詞法作用域可以訪問foo的內(nèi)部作用域,并且bar在被作為返回值賦值給baz執(zhí)行時(shí),bar函數(shù)在定義時(shí)的詞法作用域以外的地方被調(diào)用,依然可以訪問foo函數(shù)的內(nèi)部作用域變量a,這就是閉包
分析現(xiàn)在讓我們來看為什么閉包可以在定義的詞法作用域外記住并且訪問定義時(shí)的詞法作用域的變量,想要一探究竟,先來看一個(gè)簡單的例子來函數(shù)的執(zhí)行過程:
function foo (a) { console.log(a) } foo (a)
上面是一個(gè)簡單的函數(shù)調(diào)用,以及在執(zhí)行時(shí)的上下文環(huán)境,重點(diǎn)看執(zhí)行時(shí)上下文環(huán)境,在創(chuàng)建foo函數(shù)時(shí),會創(chuàng)建一個(gè)預(yù)先包含全局變量對象的作用域鏈,這個(gè)作用域鏈被保存在內(nèi)部的[[Scope]]屬性中,當(dāng)調(diào)用foo()函數(shù)時(shí),會為函數(shù)創(chuàng)建一個(gè)執(zhí)行環(huán)境,然后通過復(fù)制函數(shù)的[[Scope]屬性中的對象構(gòu)建起執(zhí)行環(huán)境的作用域鏈。此后,又有一個(gè)活動(dòng)對象(包含this、arguments、a)被創(chuàng)建并被推入執(zhí)行環(huán)境作用域鏈的前端,對于foo函數(shù)來說,其作用域鏈包含兩個(gè)變量對象,一個(gè)時(shí)全局的變量對象,一個(gè)是局部的活動(dòng)變量對象,一般來說當(dāng)函數(shù)執(zhí)行完成后,局部的活動(dòng)變量對象會被銷毀,只留全局的,但是閉包執(zhí)行過程有所不同,來看具體例子:
function foo () { var a = 2 function bar (b) { console.log(a + b) } return bar } var baz = foo() baz(3) //5
接下來來分析下上面閉包的執(zhí)行上下環(huán)境,在一個(gè)函數(shù)內(nèi)部定義的函數(shù)會將包含函數(shù)的活動(dòng)對象添加到它的作用域鏈中,因此,bar函數(shù)的作用域鏈中會包含foo函數(shù)的活動(dòng)對象,在bar函數(shù)從foo中被返回后,它的作用域鏈條被初始化為全局變量和foo中活動(dòng)對象,因此,bar函數(shù)可以訪問foo函數(shù)中定義的所有變量,同時(shí)foo函數(shù)在執(zhí)行完畢后,其活動(dòng)對象也不會被銷毀,因?yàn)閎ar函數(shù)的作用域鏈仍然在引用這個(gè)活動(dòng)對象。
說到閉包相關(guān)的問題,最典型的就是變量和this指向這兩類問題。
變量function test () { var result = new Array() for (var i = 0; i < 6; i++) { result[i] = function () { return i } } return result }
上面的代碼展示就是面試題里面經(jīng)常會碰到,result的結(jié)果從上面截圖能看到,作用域中保存的i都是6,這是為什么呢?因?yàn)殚]包保存的是函數(shù)中的活動(dòng)對象,因此它們引用的都是同一個(gè)變量,并且是變量的最后一個(gè)值,因此都是6,那這個(gè)問題怎么解決呢?最常見的最簡單肯定是將var換成let,也可以像下面這樣:
function test () { var result = new Array() for (var i = 0; i < 6; i++) { result[i] = (function () { return i })() } return result }
將閉包直接改成一個(gè)自執(zhí)行函數(shù),自執(zhí)行函數(shù)本身是沒有變量作用域的,因此會使用外層函數(shù)的變量作用域,這樣也能達(dá)到我們想要的效果
this指向var name = "window" var obj = { name: "object", getName: function () { return function () { return this.name } } } console.log(obj.getName()())
上面這段js代碼的this.name的返回值是window,這是為什么呢?按照上面寫到的,此匿名函數(shù)在執(zhí)行過程中,它的作用域會包含三部分:自身的活動(dòng)對象、getName函數(shù)的活動(dòng)對象和全局的變量對象,同時(shí)每個(gè)活動(dòng)對象自動(dòng)取得兩個(gè)特殊的變量:this和arguments,但是內(nèi)部函數(shù)在查找this時(shí)是無法直接訪問外部函數(shù)的this變量,因此會沿著作用域鏈去查找全局變量中繼續(xù)查找,如果想要取外部函數(shù)中的this取值也很簡單,只需要向下面代碼這樣:
var name = "window" var obj = { name: "object", getName: function () { var that = this return function () { return that.name } } } console.log(obj.getName()())
將this賦值給一個(gè)變量,內(nèi)部函數(shù)是可以訪問外部函數(shù)變量的,這樣就解決了
總結(jié)閉包是一個(gè)容易混淆不清的概念,這篇文章對閉包的定義、執(zhí)行、常見問題做了簡單的介紹,希望通過這篇能對大家理解和使用閉包有所幫助。如果有錯(cuò)誤或不嚴(yán)謹(jǐn)?shù)牡胤?,歡迎批評指正,如果喜歡,歡迎點(diǎn)贊。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/109262.html
摘要:匿名函數(shù)是不能單獨(dú)寫的,所以就提不上立即執(zhí)行了。六立即執(zhí)行函數(shù)在閉包中的應(yīng)用立即執(zhí)行函數(shù)能配合閉包保存狀態(tài)。來看下上節(jié)內(nèi)容中閉包的例子現(xiàn)在,我們來利用立即執(zhí)行函數(shù)來簡化它第一個(gè)匿名函數(shù)執(zhí)行完畢后,返回了第二個(gè)匿名函數(shù)。 前面的閉包中,提到與閉包相似的立即執(zhí)行函數(shù),感覺兩者還是比較容易弄混吧,嚴(yán)格來說(因?yàn)橄透叱虒﹂]包的定義不同),立即執(zhí)行函數(shù)并不屬于閉包,它不滿足閉包的三個(gè)條件。...
摘要:為了更好的理解,在閱讀此文之前建議先閱讀上一篇進(jìn)擊之詞法作用域與作用域鏈?zhǔn)裁词情]包閉包的含義就是閉合,包起來,簡單的來說,就是一個(gè)具有封閉功能與包裹功能的結(jié)構(gòu)。在中函數(shù)構(gòu)成閉包。 為了更好的理解,在閱讀此文之前建議先閱讀上一篇《進(jìn)擊JavaScript之詞法作用域與作用域鏈》 1.什么是閉包 閉包的含義就是閉合,包起來,簡單的來說,就是一個(gè)具有封閉功能與包裹功能的結(jié)構(gòu)。所謂的閉包就是...
摘要:如果是,編譯器會忽略該聲明,繼續(xù)進(jìn)行編譯否則它會要求作用域在當(dāng)前作用域的集合中聲明一個(gè)新的變量接下來編譯器會為引擎生成運(yùn)行時(shí)所需的代碼,這些代碼被用來處理這個(gè)賦值操作。引擎運(yùn)行時(shí)會首先詢問作用域,在當(dāng)前的作用域集合中是否存在一個(gè)叫做的變量。 引言 幾乎所有的編程語言都有作用域的概念,那作用域到底指的是什么呢?作用域就是編程語言在定義變量時(shí),變量如何存儲、變量如何訪問的一套規(guī)則,不同的編...
摘要:此時(shí)產(chǎn)生了閉包。導(dǎo)致,函數(shù)的活動(dòng)對象沒有被銷毀。是不是跟你想的不一樣其實(shí),這個(gè)例子重點(diǎn)就在函數(shù)上,這個(gè)函數(shù)的第一個(gè)參數(shù)接受一個(gè)函數(shù)作為回調(diào)函數(shù),這個(gè)回調(diào)函數(shù)并不會立即執(zhí)行,它會在當(dāng)前代碼執(zhí)行完,并在給定的時(shí)間后執(zhí)行。 上一節(jié)說了執(zhí)行上下文,這節(jié)咱們就乘勝追擊來搞搞閉包!頭疼的東西讓你不再頭疼! 一、函數(shù)也是引用類型的。 function f(){ console.log(not cha...
摘要:事件模型事件捕獲階段。事件到達(dá)目標(biāo)元素觸發(fā)目標(biāo)元素的監(jiān)聽函數(shù)。的狀態(tài)值與狀態(tài)碼的狀態(tài)值未初始化還沒有調(diào)用方法。載入完成已經(jīng)執(zhí)行完成,已經(jīng)接收到全部的響應(yīng)內(nèi)容。 前言 總括: 包含這三個(gè)月來碰到的一些覺得比較好的面試題,三個(gè)月沒怎么寫博客著實(shí)有些手癢,哈哈哈。7000余字,不成敬意2333 原文地址:我的前端進(jìn)階之路 知乎專欄&&簡書專題:前端進(jìn)擊者(知乎)&&前端進(jìn)擊者(簡書) 博主...
閱讀 740·2021-11-24 10:19
閱讀 1126·2021-09-13 10:23
閱讀 3445·2021-09-06 15:15
閱讀 1788·2019-08-30 14:09
閱讀 1702·2019-08-30 11:15
閱讀 1850·2019-08-29 18:44
閱讀 948·2019-08-29 16:34
閱讀 2470·2019-08-29 12:46