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