摘要:譯者按在上一篇博客,我們通過實現(xiàn)一個計數(shù)器,了解了如何使用閉包,這篇博客將提供一些代碼示例,幫助大家理解閉包。然而,如果通過代碼示例去理解閉包,則簡單很多。不過,將閉包簡單地看做局部變量,理解起來會更加簡單。
-
譯者按: 在上一篇博客,我們通過實現(xiàn)一個計數(shù)器,了解了如何使用閉包(Closure),這篇博客將提供一些代碼示例,幫助大家理解閉包。
原文: JavaScript Closures for Dummies
譯者: Fundebug
為了保證可讀性,本文采用意譯而非直譯。另外,本文版權(quán)歸原作者所有,翻譯僅用于學(xué)習(xí)。
閉包并不神奇其實,只要你領(lǐng)會了閉包的關(guān)鍵概念,一切就非常簡單了。作為JavaScript開發(fā)者,你應(yīng)該可以理解以下代碼:
Example 1function sayHello(name) { var text = "Hello " + name; var sayAlert = function() { console.log(text); } sayAlert(); } sayHello("Bob") // 輸出"Hello Bob"
在sayHello()函數(shù)中定義并調(diào)用了sayAlert()函數(shù);sayAlert()作為內(nèi)層函數(shù),可以訪問外層函數(shù)sayHello()中的text變量。理解這一點,你就可以繼續(xù)閱讀這篇博客了。
一個閉包示例兩句話總結(jié)閉包(注意,這個定義并不規(guī)范,但是有助于理解):
閉包就是函數(shù)的局部變量,這些變量在函數(shù)return之后仍然可以訪問
閉包就是函數(shù)的內(nèi)存堆棧,這個內(nèi)存堆棧在函數(shù)return之后并沒有被收回
Example 2function sayHello2(name) { var text = "Hello " + name; // 局部變量 var sayAlert = function() { console.log(text); } return sayAlert; } var say2 = sayHello2("Jane"); say2(); // 輸出"Hello Jane"
調(diào)用sayHello2()函數(shù)返回了sayAlert,它是一個引用變量,指向一個函數(shù)。相信大多數(shù)JavaScript程序員能夠理解什么是引用變量,而C程序員則可以把sayAlert以及say2理解為指向函數(shù)的指針。
C指針與JavaScript引用變量并無實質(zhì)區(qū)分。在JavaScript中,不妨這樣理解,指向函數(shù)的引用變量不僅指向函數(shù)本身,還隱含地指向了一個閉包。
代碼中匿名函數(shù)function() { alert(text); }是在另一個函數(shù),即sayHello2()中定義的。在JavaScript中,如果你在函數(shù)中定義了一個函數(shù),則創(chuàng)建了閉包。
對于C語言,以及其他絕大多數(shù)語言:函數(shù)return之后,其局部變量將無法訪問,因為內(nèi)存中的堆棧會被銷毀。
對于JavaScript,如果你在函數(shù)中定義函數(shù)的話,當(dāng)外層函數(shù)return之后,其局部變量仍然可以訪問。代碼中已經(jīng)證明了這一點:當(dāng)sayHello2()函數(shù)return之后,我們調(diào)用了say2()函數(shù),成功打印了text變量,而text變量正是sayHello2()函數(shù)的局部變量。
更多示例如果只是從定義的角度去理解閉包,顯然是非常困難。然而,如果通過代碼示例去理解閉包,則簡單很多。因此,強烈建議你認(rèn)真地理解每一個示例,弄清楚它們是如何運行的,這樣你會避免很多奇怪的BUG。
Example 3Example 3中,say667()函數(shù)return后,num變量將仍然保留在內(nèi)存中。并且,sayNumba函數(shù)中的num變量并非復(fù)制而是引用,因此它輸出的是667而非666。
function say667() { var num = 666; // say667()函數(shù)return后,num變量將仍然保留在內(nèi)存中 var sayAlert = function() { console.log(num); } num++; return sayAlert; } var sayNumba = say667(); sayNumba(); // 輸出667Example 4
Example 4中,3個全局函數(shù)gAlertNumber,gIncreaseNumber,gSetNumber指向了同一個閉包,因為它們是在同一次setupSomeGlobals()調(diào)用中聲明的。它們所指向的閉包就是setupSomeGlobals()函數(shù)的局部變量,包括了num變量。也就是說,它們操作的是同一個num變量。
function setupSomeGlobals() { var num = 666; gAlertNumber = function() { console.log(num); } gIncreaseNumber = function() { num++; } gSetNumber = function(x) { num = x; } } setupSomeGlobals(); gAlertNumber(); // 輸出666 gIncreaseNumber(); gAlertNumber(); // 輸出667 gSetNumber(5); gAlertNumber(); // 輸出5Example 5
Example 5的代碼比較難,不少人都會犯同樣的錯誤,因為它的執(zhí)行結(jié)果很可能違背了你的直覺。
function buildList(list) { var result = []; for (var i = 0; i < list.length; i++) { var item = "item" + list[i]; result.push( function() { console.log(item + " " + list[i])} ); } return result; } var fnlist = buildList([1,2,3]); for (var j = 0; j < fnlist.length; j++) { fnlist[j](); // 連續(xù)輸出3個"item3 undefined" }
result.push( function() {alert(item + " " + list[i])}將指向匿名函數(shù)function() {alert(item + " " + list[i])}的引用變量加入了數(shù)組,其效果等價于:
pointer = function() {alert(item + " " + list[i])}; result.push(pointer);
代碼執(zhí)行后,連續(xù)輸出了3個"item3 undefined",明顯與直覺不同。
調(diào)用buildList()函數(shù)之后,我們得到了一個數(shù)組,數(shù)組中有3個函數(shù),而這3個函數(shù)指向了同一個閉包。而閉包中的item變量值為"item3",i變量值為3。如果理解了3個函數(shù)指向的是同一個閉包,則輸出結(jié)果就不難理解了。
Example 6Example 6中,alice變量在sayAlert函數(shù)之后定義,這并未影響代碼執(zhí)行。因為返回函數(shù)sayAlice2所指向的閉包會包含sayAlice()函數(shù)中的所有局部變量,這自然包括了alice變量,因此可以正常打印"Hello Alice"。
function sayAlice() { var sayAlert = function() { console.log(alice); } var alice = "Hello Alice"; return sayAlert; } var sayAlice2 = sayAlice(); sayAlice2(); // 輸出"Hello Alice"Example 7
由Example 7可知,每次調(diào)用newClosure()都會創(chuàng)建獨立的閉包,它們的局部變量num與ref的值并不相同。
function newClosure(someNum, someRef) { var anArray = [1,2,3]; var num = someNum; var ref = someRef; return function(x) { num += x; anArray.push(num); console.log("num: " + num + "; " + "anArray " + anArray.toString() + "; " + "ref.someVar " + ref.someVar); } } closure1 = newClosure(40, {someVar: "closure 1"}); closure2 = newClosure(1000, {someVar: "closure 2"}); closure1(5); // 打印"num: 45; anArray 1,2,3,45; ref.someVar closure 1" closure2(-10); // 打印"num: 990; anArray 1,2,3,990; ref.someVar closure 2"總結(jié)
嚴(yán)格來講,我對閉包的解釋并不準(zhǔn)確。不過,將閉包簡單地看做局部變量,理解起來會更加簡單。
參考鏈接Private Members in JavaScript
Memory Leakage in Internet Explorer
版權(quán)聲明:
轉(zhuǎn)載時請注明作者Fundebug以及本文地址:
https://blog.fundebug.com/201...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/91419.html
摘要:一前言這個周末,注意力都在學(xué)習(xí)基礎(chǔ)知識上面,剛好看到了閉包這個神圣的東西,所以打算把這兩天學(xué)到的總結(jié)下來,算是鞏固自己所學(xué)。因此要注意閉包的使用,否則會導(dǎo)致性能問題。五總結(jié)閉包的作用能夠讀取其他函數(shù)內(nèi)部變量。 # 一、前言 這個周末,注意力都在學(xué)習(xí)基礎(chǔ)Js知識上面,剛好看到了閉包這個神圣的東西,所以打算把這兩天學(xué)到的總結(jié)下來,算是鞏固自己所學(xué)。也可能有些不正確的地方,也請大家看到了,麻...
摘要:但是,必須強調(diào),閉包是一個運行期概念。通過原型鏈可以實現(xiàn)繼承,而與閉包相關(guān)的就是作用域鏈。常理來說,一個函數(shù)執(zhí)行完畢,其執(zhí)行環(huán)境的作用域鏈會被銷毀。所以此時,的作用域鏈雖然銷毀了,但是其活動對象仍在內(nèi)存中。 學(xué)習(xí)Javascript閉包(Closure)javascript的閉包JavaScript 閉包深入理解(closure)理解 Javascript 的閉包JavaScript ...
摘要:在中函數(shù)是一等對象,它們不被聲明為任何東西的一部分,而所引用的對象稱為函數(shù)上下文并不是由聲明函數(shù)的方式?jīng)Q定的,而是由調(diào)用函數(shù)的方式?jīng)Q定的。更為準(zhǔn)確的表述應(yīng)該為當(dāng)對象充當(dāng)函數(shù)的調(diào)用函數(shù)上下文時,函數(shù)就充當(dāng)了對象的方法。 引言:當(dāng)理解了對象和函數(shù)的基本概念,你可能會發(fā)現(xiàn),在JavaScript中有很多原以為理所當(dāng)然(或盲目接受)的事情開始變得更有意義了。 1.JavaScript...
摘要:也許最好的理解是閉包總是在進入某個函數(shù)的時候被創(chuàng)建,而局部變量是被加入到這個閉包中。在函數(shù)內(nèi)部的函數(shù)的內(nèi)部聲明函數(shù)是可以的可以獲得不止一個層級的閉包。 前言 總括 :這篇文章使用有效的javascript代碼向程序員們解釋了閉包,大牛和功能型程序員請自行忽略。 譯者 :文章寫在2006年,可直到翻譯的21小時之前作者還在完善這篇文章,在Stackoverflow的How do Java...
摘要:權(quán)威指南第六版關(guān)于閉包的說明采用詞法作用域,也就是說函數(shù)的執(zhí)行依賴于變量的作用域,這個作用域是在函數(shù)定義時決定的,而不是函數(shù)調(diào)用時決定的。閉包這個術(shù)語的來源指函數(shù)變量可以被隱藏于作用域鏈之內(nèi),因此看起來是函數(shù)將變量包裹了起來。 最近打算換工作,所以參加了幾次面試(國內(nèi)比較知名的幾家互聯(lián)網(wǎng)公司)。在面試的過程中每當(dāng)被問起閉包,我都會說閉包是作用域的問題?令人驚訝的是幾乎無一例外的當(dāng)我提到...
閱讀 4278·2021-10-13 09:39
閱讀 482·2021-09-06 15:02
閱讀 3229·2019-08-30 15:53
閱讀 1040·2019-08-30 13:04
閱讀 2029·2019-08-30 11:27
閱讀 2010·2019-08-26 13:51
閱讀 2092·2019-08-26 11:33
閱讀 2902·2019-08-26 10:36