摘要:權(quán)威指南第六版關(guān)于閉包的說(shuō)明采用詞法作用域,也就是說(shuō)函數(shù)的執(zhí)行依賴于變量的作用域,這個(gè)作用域是在函數(shù)定義時(shí)決定的,而不是函數(shù)調(diào)用時(shí)決定的。閉包這個(gè)術(shù)語(yǔ)的來(lái)源指函數(shù)變量可以被隱藏于作用域鏈之內(nèi),因此看起來(lái)是函數(shù)將變量包裹了起來(lái)。
最近打算換工作,所以參加了幾次面試(國(guó)內(nèi)比較知名的幾家互聯(lián)網(wǎng)公司)。在面試的過(guò)程中每當(dāng)被問(wèn)起閉包,我都會(huì)說(shuō)閉包是作用域的問(wèn)題?令人驚訝的是幾乎無(wú)一例外的當(dāng)我提到作用域時(shí)我都被打斷,并提醒我好好的找一本javascript的書籍看看。而當(dāng)我忍不住去問(wèn)面試官對(duì)于閉包你是怎么理解的?我得到的大多是回答都是通過(guò)返回一個(gè)函數(shù),然后通過(guò)這個(gè)函數(shù)來(lái)訪問(wèn)局部變量(私有變量),有的還會(huì)扯上聲明提前,this指向等。聽(tīng)到這些,心理默默滴血,沒(méi)錯(cuò),我只是菜鳥。
看到有這么多人不理解閉包,所以我不得不再次的提及,如果有誤,歡迎指正。
閉包只是為了實(shí)現(xiàn)詞法作用域而用到的一種數(shù)據(jù)結(jié)構(gòu)而已先從阮一峰09年寫的一篇關(guān)于閉包的文章開始(原文地址)文中說(shuō)"可以把閉包理解為就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù),因?yàn)閖s中,只有函數(shù)內(nèi)部的子函數(shù)才能讀取局部變量,因此也可以把閉包定義在一個(gè)函數(shù)內(nèi)部的函數(shù)。所以閉包本質(zhì)就是將函數(shù)內(nèi)部和函數(shù)外部連接起來(lái)的一座橋梁"。
畢竟不是專業(yè)學(xué)習(xí)js的,也不是程序語(yǔ)言方面的專家,在這里就不去計(jì)較了,下文會(huì)給出更完整的閉包說(shuō)明。(PS:個(gè)人還是比較欣賞阮一峰寫的文章的,能將一些概念講得通俗易懂)
最后還留下了一個(gè)思考題
示例01:
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ return function(){ return this.name; }; } }; alert(object.getNameFunc()());
示例02:
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ var that = this; return function(){ return that.name; }; } }; alert(object.getNameFunc()());
看完這道題,就希望大家將this和閉包分開,不要給自己找麻煩?
在開始說(shuō)閉包之前,需要理解好以下的概念:
詞法作用域(靜態(tài)作用域)
函數(shù)上下文
之前簡(jiǎn)單的提過(guò),詞法作用域簡(jiǎn)單的理解就是函數(shù)的上下文是在聲明是確定的,而不是在調(diào)用時(shí)確定的。這里不想對(duì)這兩個(gè)名詞作過(guò)多的解釋,我們知道js/ruby/python等主流的語(yǔ)言都是詞法作用域就好,因?yàn)榕c之相對(duì)的動(dòng)態(tài)作用域有許多的問(wèn)題,所以現(xiàn)在的語(yǔ)言基本都是詞法作用域的。
函數(shù)上下文就簡(jiǎn)單的理解為函數(shù)執(zhí)行的環(huán)境好了,在這個(gè)環(huán)境中保存了函數(shù)執(zhí)行所需的變量。
JavaScript權(quán)威指南第六版關(guān)于閉包的說(shuō)明JavaScript采用詞法作用域,也就是說(shuō)函數(shù)的執(zhí)行依賴于變量的作用域,這個(gè)作用域是在函數(shù)定義時(shí)決定的,而不是函數(shù)調(diào)用時(shí)決定的。為了實(shí)現(xiàn)詞法作用域,JavaScript函數(shù)對(duì)象的內(nèi)部狀態(tài)不僅包含函數(shù)的代碼邏輯,還必須引用當(dāng)前的作用域鏈。函數(shù)對(duì)象可以通過(guò)作用域鏈相互關(guān)聯(lián)起來(lái),函數(shù)體內(nèi)部的變量都可以保存在函數(shù)作用域內(nèi),這種特性在計(jì)算機(jī)科學(xué)文獻(xiàn)中稱為"閉包"。
當(dāng)定義一個(gè)函數(shù)時(shí),它實(shí)際上保存了一個(gè)作用域鏈。當(dāng)調(diào)用這個(gè)函數(shù)時(shí),它創(chuàng)建
一個(gè)新的對(duì)象來(lái)存儲(chǔ)它的局部變量,并將這個(gè)對(duì)象添加到保存的作用域鏈上。
(閉包可以簡(jiǎn)單的理解為函數(shù)用來(lái)存儲(chǔ)它的局部變量的對(duì)象,這個(gè)對(duì)象我們來(lái)說(shuō)是不可見(jiàn)的,是js解釋器實(shí)現(xiàn)的過(guò)程中才會(huì)用到的。每一個(gè)函數(shù)都會(huì)有這樣的一個(gè)對(duì)象,作用域鏈則是這些對(duì)象之間的關(guān)系。)
"閉包"這個(gè)術(shù)語(yǔ)的來(lái)源:指函數(shù)變量可以被隱藏于作用域鏈之內(nèi),因此看起來(lái)是函數(shù)將變量"包裹"了起來(lái)。
看文字可能會(huì)比較繞,下面是書中的一個(gè)例子:
var scope = "global scope"; /*全局變量*/ function checkscope(){ var scope = "local scope"; /*局部變量*/ function f(){return scope;} return f(); } checkscope(); /*=>"local scope"*/
var scope = "global scope"; /*全局變量*/ function checkscope(){ var scope = "local scope"; /*局部變量*/ function f(){return scope;} return f; } checkscope()(); /*=>"local scope"*/
checkscope最后的返回值都是一樣的,即"local scope"。
上面兩個(gè)例子的不同之處就是函數(shù)f執(zhí)行的地方不同,一個(gè)在checkscope這個(gè)作用域里調(diào)用的(每一個(gè)函數(shù)都是一個(gè)作用域),一個(gè)在全局作用域里調(diào)用的。回憶之前說(shuō)的,函數(shù)調(diào)用時(shí)的上下文或者說(shuō)作用域是在聲明時(shí)確定的,所以與調(diào)用的位置無(wú)關(guān),即f函數(shù)的調(diào)用位置雖然不同,調(diào)用的環(huán)境雖然不同,但最終的結(jié)果都是一樣的。
說(shuō)到這里,閉包就說(shuō)得差不多了,回過(guò)頭來(lái)看一下常規(guī)的對(duì)于閉包的理解:
通過(guò)返回函數(shù)的形式取得函數(shù)的局部變量。
這種說(shuō)法本身沒(méi)有錯(cuò),但它只是閉包的一種表現(xiàn)形式,
比如將上例進(jìn)行下更改:
var scope = "global scope"; /*全局變量*/ function checkscope(fn){ var scope = "local scope"; /*局部變量*/ function f(){return scope;} fn(f); } checkscope(function(func){ var scope = "func scope"; return func(); }); /*=>"local scope"*/
改成類似回調(diào)的執(zhí)行方式,結(jié)果還是一樣的,注意結(jié)果并不是func scope,但是并沒(méi)有返回f函數(shù)這一說(shuō),難道這就不是閉包了嗎?(當(dāng)然這里有點(diǎn)扣字眼)
說(shuō)說(shuō)this想起最開始時(shí)的那個(gè)思考題了嗎?與閉包就沒(méi)什么關(guān)系(注:任何一個(gè)函數(shù)其實(shí)都用到了閉包,但我們暫且考慮兩層以及兩層以上的嵌套情況,未嵌套情況下因?yàn)槭褂玫亩际侨肿饔糜?,結(jié)果應(yīng)該是很直觀的)。this一般用來(lái)代表函數(shù)的調(diào)用對(duì)象,它和上下文對(duì)象并不是同一個(gè),上下文對(duì)象對(duì)我們來(lái)說(shuō)是不可見(jiàn)的,除了全局作用域。
示例01:
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ return function(){ return this.name; }; } }; alert(object.getNameFunc()()); /*The Window*/
示例02:
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ var that = this; return function(){ return that.name; }; } }; var that = {name: "xiaofu"}; /*干擾項(xiàng)*/ alert(object.getNameFunc()()); /*My Object*/
我們?cè)賮?lái)看一下題目,示例01輸出的是"The Window",示例02輸出的是"My Object"。
說(shuō)明:
示例01最終執(zhí)行的是 function(){return this.name},因?yàn)闆](méi)有顯示指明調(diào)用對(duì)象,所以其this執(zhí)行全局對(duì)象。
示例02先調(diào)用object.getNameFunc(),因?yàn)轱@示的指定了調(diào)用對(duì)象,所以內(nèi)部的this是object(注:這里說(shuō)的是this指向的問(wèn)題,還沒(méi)有說(shuō)閉包),接著執(zhí)行function (){return that.name},這個(gè)函數(shù)在getNameFunc這個(gè)函數(shù)作用域中聲明的,所以它調(diào)用的時(shí)候使用的是這個(gè)作用域,即得到var that = this;的這個(gè)that;而不是外面的that。
作用域鏈不等同于原型鏈真不知道這兩個(gè)不相關(guān)的東西怎么會(huì)被等同起來(lái)看待,以后誰(shuí)告訴我它們是同一個(gè)東西,我就想問(wèn)了,ruby、python這種沒(méi)有原型概念的語(yǔ)言難道就沒(méi)有作用域鏈了嗎?
更有甚者將變量聲明提升和閉包混在一起,也是醉了。
總結(jié)閉包:函數(shù)執(zhí)行時(shí)變量的獲取從聲明的作用域處去獲取(注意鏈?zhǔn)疥P(guān)系,當(dāng)前沒(méi)有就往父級(jí)找,知道全局作用域)
this:顯示指定調(diào)用者則this就指向誰(shuí),如未指定則為全局對(duì)象
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/87715.html
摘要:而閉包的神奇之處正是可以阻止事情的發(fā)生。拜所聲明的位置所賜,它擁有涵蓋內(nèi)部作用域的閉包,使得該作用域能夠一直存活,以供在之后任何時(shí)間進(jìn)行引用。依然持有對(duì)該作用域的引用,而這個(gè)引用就叫閉包。 引子 先看一個(gè)問(wèn)題,下面兩個(gè)代碼片段會(huì)輸出什么? // Snippet 1 a = 2; var a; console.log(a); // Snippet 2 console.log(a); v...
摘要:一言以蔽之,閉包,你就得掌握。當(dāng)函數(shù)記住并訪問(wèn)所在的詞法作用域,閉包就產(chǎn)生了。所以閉包才會(huì)得以實(shí)現(xiàn)。從技術(shù)上講,這就是閉包。執(zhí)行后,他的內(nèi)部作用域并不會(huì)消失,函數(shù)依然保持有作用域的閉包。 網(wǎng)上總結(jié)閉包的文章已經(jīng)爛大街了,不敢說(shuō)筆者這篇文章多么多么xxx,只是個(gè)人理解總結(jié)。各位看官瞅瞅就好,大神還希望多多指正。此篇文章總結(jié)與《JavaScript忍者秘籍》 《你不知道的JavaScri...
摘要:吐槽一下,閉包這個(gè)詞的翻譯真是有很大的誤解性啊要說(shuō)閉包,要先說(shuō)下詞法作用域。閉包兩個(gè)作用通過(guò)閉包,在外部環(huán)境訪問(wèn)內(nèi)部環(huán)境的變量。閉包使得函數(shù)可以繼續(xù)訪問(wèn)定義時(shí)的詞法作用域。 閉包是真的讓人頭暈啊,看了很久還是覺(jué)得很模糊。只能把目前自己的一些理解先寫下來(lái),這其中必定包含著一些錯(cuò)誤,待日后有更深刻的理解時(shí)再作更改。 吐槽一下,閉包這個(gè)詞的翻譯真是有很大的誤解性啊…… 要說(shuō)閉包,要先說(shuō)下詞法...
摘要:寫在前面對(duì)于一個(gè)前端開發(fā)者,應(yīng)該沒(méi)有不知道作用域的。欺騙詞法作用域有兩個(gè)機(jī)制可以欺騙詞法作用域和。關(guān)于你不知道的的第一部分作用域和閉包已經(jīng)結(jié)束了,但是,更新不會(huì)就此止住未完待續(xù) 這是《你不知道的JavaScript》的第一部分。 本系列持續(xù)更新中,Github 地址請(qǐng)查閱這里。 寫在前面 對(duì)于一個(gè)前端開發(fā)者,應(yīng)該沒(méi)有不知道作用域的。它是一個(gè)既簡(jiǎn)單有復(fù)雜的概念,簡(jiǎn)單到每行代碼都有它的影子...
摘要:再看一段代碼這樣就清晰地展示了閉包的詞法作用域能訪問(wèn)的作用域?qū)?dāng)做一個(gè)值返回執(zhí)行后,將的引用賦值給執(zhí)行,輸出了變量我們知道通過(guò)引用的關(guān)系,就是函數(shù)本身。 在正式學(xué)習(xí)閉包之前,請(qǐng)各位同學(xué)一定要確保自己對(duì)詞法作用域已經(jīng)非常的熟悉了,如果對(duì)詞法作用域還不夠熟悉的話,可以先看: 深入理解閉包之前置知識(shí)---作用域與詞法作用域 前言 現(xiàn)在去面試前端開發(fā)的崗位,如果你的面試官也是個(gè)前端,并且不是太...
閱讀 3189·2021-09-28 09:36
閱讀 3723·2021-09-08 09:45
閱讀 1865·2021-09-01 10:43
閱讀 3511·2019-08-30 12:44
閱讀 3382·2019-08-29 17:25
閱讀 1398·2019-08-29 11:03
閱讀 2017·2019-08-26 13:36
閱讀 723·2019-08-23 18:24