摘要:但是閉包也不是什么復(fù)雜到不可理解的東西,簡而言之,閉包就是閉包就是函數(shù)的局部變量集合,只是這些局部變量在函數(shù)返回后會繼續(xù)存在。可惜的是,并沒有提供相關(guān)的成員和方法來訪問閉包中的局部變量。
(收藏自 技術(shù)狂)
前言:還是一篇入門文章。Javascript中有幾個非常重要的語言特性——對象、原型繼承、閉包。其中閉包 對于那些使用傳統(tǒng)靜態(tài)語言C/C++的程序員來說是一個新的語言特性。本文將以例子入手來介紹Javascript閉包的語言特性,并結(jié)合一點(diǎn) ECMAScript語言規(guī)范來使讀者可以更深入的理解閉包。
注:本文是入門文章,例子素材整理于網(wǎng)絡(luò),如果你是高手,歡迎針對文章提出技術(shù)性建議和意見。本文討論的是Javascript,不想做語言對比,如果您對Javascript天生不適,請自行繞道。
什么是閉包閉包是什么?閉包是Closure,這是靜態(tài)語言所不具有的一個新特性。但是閉包也不是什么復(fù)雜到不可理解的東西,簡而言之,閉包就是:
閉包就是函數(shù)的局部變量集合,只是這些局部變量在函數(shù)返回后會繼續(xù)存在。
閉包就是就是函數(shù)的“堆?!痹诤瘮?shù)返回后并不釋放,我們也可以理解為這些函數(shù)堆棧并不在棧上分配而是在堆上分配
當(dāng)在一個函數(shù)內(nèi)定義另外一個函數(shù)就會產(chǎn)生閉包
上面的第二定義是第一個補(bǔ)充說明,抽取第一個定義的主謂賓——閉包是函數(shù)的‘局部變量’集合。只是這個局部變量是可以在函數(shù)返回后被訪問。(這個不是官方定義,但是這個定義應(yīng)該更有利于你理解閉包)
做為局部變量都可以被函數(shù)內(nèi)的代碼訪問,這個和靜態(tài)語言是沒有差別。閉包的差別在于局部變變量可以在函數(shù)執(zhí)行結(jié)束后仍然被函數(shù)外的代碼訪問。這意味 著函數(shù)必須返回一個指向閉包的“引用”,或?qū)⑦@個”引用”賦值給某個外部變量,才能保證閉包中局部變量被外部代碼訪問。當(dāng)然包含這個引用的實(shí)體應(yīng)該是一個 對象,因?yàn)樵贘avascript中除了基本類型剩下的就都是對象了??上У氖?,ECMAScript并沒有提供相關(guān)的成員和方法來訪問閉包中的局部變 量。但是在ECMAScript中,函數(shù)對象中定義的內(nèi)部函數(shù)(inner function)是可以直接訪問外部函數(shù)的局部變量,通過這種機(jī)制,我們就可以以如下的方式完成對閉包的訪問了。
function greeting(name) { var text = "Hello " + name; // local variable // 每次調(diào)用時,產(chǎn)生閉包,并返回內(nèi)部函數(shù)對象給調(diào)用者 return function() { alert(text); } } var sayHello=greeting("Closure"); sayHello() // 通過閉包訪問到了局部變量text
上述代碼的執(zhí)行結(jié)果是:Hello Closure,因?yàn)閟ayHello()函數(shù)在greeting函數(shù)執(zhí)行完畢后,仍然可以訪問到了定義在其之內(nèi)的局部變量text。
好了,這個就是傳說中閉包的效果,閉包在Javascript中有多種應(yīng)用場景和模式,比如Singleton,Power Constructor等這些Javascript模式都離不開對閉包的使用。
ECMAScript閉包模型ECMAScript到底是如何實(shí)現(xiàn)閉包的呢?想深入了解的親們可以獲取ECMAScript 規(guī)范進(jìn)行研究,我這里也只做一個簡單的講解,內(nèi)容也是來自于網(wǎng)絡(luò)。
在ECMAscript的腳本的函數(shù)運(yùn)行時,每個函數(shù)關(guān)聯(lián)都有一個執(zhí)行上下文場景(Execution Context)?,這個執(zhí)行上下文場景中包含三個部分
文法環(huán)境(The LexicalEnvironment)
變量環(huán)境(The VariableEnvironment)
this綁定
其中第三點(diǎn)this綁定與閉包無關(guān),不在本文中討論。文法環(huán)境中用于解析函數(shù)執(zhí)行過程使用到的變量標(biāo)識符。我們可以將文法環(huán)境想象成一個對象,該對 象包含了兩個重要組件,環(huán)境記錄(Enviroment Recode),和外部引用(指針)。環(huán)境記錄包含包含了函數(shù)內(nèi)部聲明的局部變量和參數(shù)變量,外部引用指向了外部函數(shù)對象的上下文執(zhí)行場景。全局的上下文 場景中此引用值為NULL。這樣的數(shù)據(jù)結(jié)構(gòu)就構(gòu)成了一個單向的鏈表,每個引用都指向外層的上下文場景。
例如上面我們例子的閉包模型應(yīng)該是這樣,sayHello函數(shù)在最下層,上層是函數(shù)greeting,最外層是全局場景。如下圖: 因此當(dāng)sayHello被調(diào)用的時候,sayHello會通過上下文場景找到局部變量text的值,因此在屏幕的對話框中顯示出”Hello Closure” 變量環(huán)境(The VariableEnvironment)和文法環(huán)境的作用基本相似,具體的區(qū)別請參看ECMAScript的規(guī)范文檔。
閉包的樣列前面的我大致了解了Javascript閉包是什么,閉包在Javascript是怎么實(shí)現(xiàn)的。下面我們通過針對一些例子來幫助大家更加深入的理解閉包,下面共有5個樣例,例子來自于JavaScript Closures For Dummies(鏡像)。
例子1:閉包中局部變量是引用而非拷貝
function say667() { // Local variable that ends up within closure var num = 666; var sayAlert = function() { alert(num); } num++; return sayAlert; } var sayAlert = say667(); sayAlert()
因此執(zhí)行結(jié)果應(yīng)該彈出的667而非666。
例子2:多個函數(shù)綁定同一個閉包,因?yàn)樗麄兌x在同一個函數(shù)內(nèi)。
function setupSomeGlobals() { // Local variable that ends up within closure var num = 666; // Store some references to functions as global variables gAlertNumber = function() { alert(num); } gIncreaseNumber = function() { num++; } gSetNumber = function(x) { num = x; } } setupSomeGlobals(); // 為三個全局變量賦值 gAlertNumber(); //666 gIncreaseNumber(); gAlertNumber(); // 667 gSetNumber(12);// gAlertNumber();//12
例子3:當(dāng)在一個循環(huán)中賦值函數(shù)時,這些函數(shù)將綁定同樣的閉包
function buildList(list) { var result = []; for (var i = 0; i < list.length; i++) { var item = "item" + list[i]; result.push( function() {alert(item + " " + list[i])} ); } return result; } function testList() { var fnlist = buildList([1,2,3]); // using j only to help prevent confusion - could use i for (var j = 0; j < fnlist.length; j++) { fnlist[j](); } }
testList的執(zhí)行結(jié)果是彈出item3 undefined窗口三次,因?yàn)檫@三個函數(shù)綁定了同一個閉包,而且item的值為最后計算的結(jié)果,但是當(dāng)i跳出循環(huán)時i值為4,所以list[4]的結(jié)果為undefined.
例子4:外部函數(shù)所有局部變量都在閉包內(nèi),即使這個變量聲明在內(nèi)部函數(shù)定義之后。
function sayAlice() { var sayAlert = function() { alert(alice); } // Local variable that ends up within closure var alice = "Hello Alice"; return sayAlert; } var helloAlice=sayAlice(); helloAlice();
執(zhí)行結(jié)果是彈出”Hello Alice”的窗口。即使局部變量聲明在函數(shù)sayAlert之后,局部變量仍然可以被訪問到。
例子5:每次函數(shù)調(diào)用的時候創(chuàng)建一個新的閉包
function newClosure(someNum, someRef) { // Local variables that end up within closure var num = someNum; var anArray = [1,2,3]; var ref = someRef; return function(x) { num += x; anArray.push(num); alert("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 closure1" closure2(-10);// num:990 anArray[1,2,3,990] ref:"someVar closure2"閉包的應(yīng)用
Singleton 單件:
var singleton = function () { var privateVariable; function privateFunction(x) { ...privateVariable... } return { firstMethod: function (a, b) { ...privateVariable... }, secondMethod: function (c) { ...privateFunction()... } }; }();
這個單件通過閉包來實(shí)現(xiàn)。通過閉包完成了私有的成員和方法的封裝。匿名主函數(shù)返回一個對象。對象包含了兩個方法,方法1可以方法私有變量,方法2訪 問內(nèi)部私有函數(shù)。需要注意的地方是匿名主函數(shù)結(jié)束的地方的’()’,如果沒有這個’()’就不能產(chǎn)生單件。因?yàn)槟涿瘮?shù)只能返回了唯一的對象,而且不能被 其他地方調(diào)用。這個就是利用閉包產(chǎn)生單件的方法。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/87676.html
摘要:如何在初學(xué)就理解閉包你需要接著讀下去。這樣定義閉包是函數(shù)和聲明該函數(shù)的詞法環(huán)境的組合。小結(jié)閉包在中隨處可見。閉包是中的精華部分,理解它需要具備一定的作用域執(zhí)行棧的知識。 這是本系列的第 4 篇文章。 作為 JS 初學(xué)者,第一次接觸閉包的概念是因?yàn)閷懗隽祟愃葡旅娴拇a: for (var i = 0; i < helpText.length; i++) { var item = he...
摘要:也許最好的理解是閉包總是在進(jìn)入某個函數(shù)的時候被創(chuàng)建,而局部變量是被加入到這個閉包中。在函數(shù)內(nèi)部的函數(shù)的內(nèi)部聲明函數(shù)是可以的可以獲得不止一個層級的閉包。 前言 總括 :這篇文章使用有效的javascript代碼向程序員們解釋了閉包,大牛和功能型程序員請自行忽略。 譯者 :文章寫在2006年,可直到翻譯的21小時之前作者還在完善這篇文章,在Stackoverflow的How do Java...
摘要:當(dāng)面試中讓我解釋一下閉包時我懵逼了。這個解釋開始可能有點(diǎn)晦澀,讓我們抽絲剝繭摘下閉包的真面目。此文不詳述作用域有專門的主題闡述,不過作用域是理解閉包原理的基礎(chǔ)。這才是閉包的真正便利之處。閉包使用不當(dāng)就會很坑。 原文鏈接 為什么深度學(xué)習(xí)JavaScript? JavaScript如今是最流行的編程語言之一。它運(yùn)行在瀏覽器、服務(wù)器、移動設(shè)備、桌面應(yīng)用,也可能包括冰箱。無需我舉其他再多不相干...
摘要:譯者按在上一篇博客,我們通過實(shí)現(xiàn)一個計數(shù)器,了解了如何使用閉包,這篇博客將提供一些代碼示例,幫助大家理解閉包。然而,如果通過代碼示例去理解閉包,則簡單很多。不過,將閉包簡單地看做局部變量,理解起來會更加簡單。 - 譯者按: 在上一篇博客,我們通過實(shí)現(xiàn)一個計數(shù)器,了解了如何使用閉包(Closure),這篇博客將提供一些代碼示例,幫助大家理解閉包。 原文: JavaScript Clos...
摘要:閉包引起的內(nèi)存泄漏總結(jié)從理論的角度將由于作用域鏈的特性中所有函數(shù)都是閉包但是從應(yīng)用的角度來說只有當(dāng)函數(shù)以返回值返回或者當(dāng)函數(shù)以參數(shù)形式使用或者當(dāng)函數(shù)中自由變量在函數(shù)外被引用時才能成為明確意義上的閉包。 文章同步到github js的閉包概念幾乎是任何面試官都會問的問題,最近把閉包這塊的概念梳理了一下,記錄成以下文章。 什么是閉包 我先列出一些官方及經(jīng)典書籍等書中給出的概念,這些概念雖然...
閱讀 1839·2021-11-11 16:55
閱讀 761·2019-08-30 15:53
閱讀 3600·2019-08-30 15:45
閱讀 748·2019-08-30 14:10
閱讀 3277·2019-08-30 12:46
閱讀 2134·2019-08-29 13:15
閱讀 2035·2019-08-26 13:48
閱讀 943·2019-08-26 12:23