版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、原文:Richard Cornford. March 2004.翻譯:大漠窮秋 2010-11-23腳本娃娃團(tuán)隊(duì)出品介紹閉包閉包是一個(gè)表達(dá)式(通常是一個(gè)函數(shù)),可以有任意參數(shù),連同綁定這些參數(shù)的環(huán)境(它“封閉”了表達(dá)式)一起構(gòu)成。 閉包是ECMAScript (javascript)最強(qiáng)大的特性之一,但是不理解就無法正確地利用它。然而,創(chuàng)建它們相對(duì)容易,甚至無意間就可以創(chuàng)建,創(chuàng)建它們會(huì)在造成潛在的有害后果,尤其在一些相對(duì)通用的瀏覽器環(huán)境中。為了避免無意間遭遇這些弊端,并利用它們提供的便利,非常有必要理解其機(jī)制。這在很大成程度上取決于標(biāo)識(shí)符解析過程中作用域鏈所扮演的角色,以及對(duì)象屬性名解析方面的
2、協(xié)議。閉包的簡單解釋是,ECMAScript支持內(nèi)部函數(shù),函數(shù)定義和函數(shù)表達(dá)式位于其它函數(shù)體內(nèi)部。這樣就允許這些內(nèi)部函數(shù)訪問所有局部變量、(外部)函數(shù)的參數(shù)和外部函數(shù)中聲明的(其它)內(nèi)部函數(shù)。當(dāng)這些內(nèi)部函數(shù)之一可以在其所屬的(外部)函數(shù)之外被訪問時(shí),就形成了一個(gè)閉包。這樣,在外部函數(shù)返回之后它仍然可以執(zhí)行。這時(shí)它仍然可以訪問局部變量、參數(shù)和外部函數(shù)中聲明的其它內(nèi)部函數(shù)。當(dāng)外部函數(shù)返回之后,這些局部變量、(外部)函數(shù)參數(shù)和函數(shù)定義(最初)仍然持有它們擁有的值,并且可以與內(nèi)部函數(shù)交互。 不幸的是,正確理解閉包需要理解它們背后的機(jī)制,以及相當(dāng)多的技術(shù)細(xì)節(jié)。雖然在以下描述的開頭部分,ECMA 262指
3、定的一些算法已經(jīng)被廢棄了,但是很多不能省略或者簡單解釋。熟悉對(duì)象屬性名解析的個(gè)人可以跳過以下小節(jié),并且可以停止閱讀此文檔然后回去運(yùn)用它們。對(duì)象屬性名決議CMAScript承認(rèn)兩種類型的對(duì)象,“本地對(duì)象”和“宿主對(duì)象”("Native Object" and "Host Object")以及一個(gè)本地對(duì)象的子集,叫做“內(nèi)置對(duì)象”("Built-in Object")(ECMA262第三版4.3節(jié))。本地對(duì)象屬于語言,宿主對(duì)象由環(huán)境提供,例如可以是document對(duì)象、DOM節(jié)點(diǎn)等等。本地對(duì)象是松散的,并且是命名屬性的動(dòng)態(tài)包(當(dāng)涉及到內(nèi)置對(duì)
4、象的子集時(shí),一些實(shí)現(xiàn)不是那么動(dòng)態(tài),但是這通常無關(guān)緊要)。對(duì)象定義的命名屬性將會(huì)持有一個(gè)值,它可以是到另一個(gè)對(duì)象的引用(從這個(gè)意義上說函數(shù)也是對(duì)象),或者是一個(gè)原始值:String,Number,Boolean,Null或者Undefined。原生類型Undefined有點(diǎn)奇怪,利用它可以把一個(gè)對(duì)象的屬性賦值為Undefined,但是這么做不會(huì)從對(duì)象上刪除這個(gè)屬性;它保持為一個(gè)已定義的命名屬性,僅僅持有了一個(gè)為undefined的值。以下是一個(gè)簡單描述,屬性值是如何在對(duì)象上設(shè)置和讀取的,盡可能最大限度地省略內(nèi)部細(xì)節(jié)。賦值通過給這個(gè)命名屬性指定一個(gè)值,可以創(chuàng)建對(duì)象的命名屬性,或者為已存在的命名屬性
5、賦值。例如給定:var objectRef = new Object(); /創(chuàng)建一個(gè)普通的javascript對(duì)象。一個(gè)名為"testNumber"的屬性可以這樣創(chuàng)建:objectRef.testNumber = 5;/* 或者*/objectRef"testNumber" = 5;在賦值之前,對(duì)象沒有"testNumber"屬性,但是在賦值之后會(huì)創(chuàng)建一個(gè)。所有后續(xù)賦值都沒有必要?jiǎng)?chuàng)建屬性,僅僅重新設(shè)置它的值:objectRef.testNumber = 8;/* 或者*/objectRef"testNumber"
6、 = 8;Javascript對(duì)象的屬性自身也可以是對(duì)象,稍后詳述,并且原型(prototype)也可以擁有命名屬性。但是它在賦值的時(shí)候不起作用。如果賦值的時(shí)候,實(shí)際的對(duì)象沒有相應(yīng)名稱的屬性,一個(gè)擁有此名稱的屬性將會(huì)被創(chuàng)建,并且把值賦給它。如果它已經(jīng)擁有此屬性則重新賦值。讀取值從對(duì)象屬性上讀取值時(shí)原型開始起作用。如果一個(gè)對(duì)象擁有一個(gè)屬性,與屬性訪問器(譯者注,指的就是屬性訪問操作符)的屬性名相同,則返回這個(gè)屬性的值: /*給一個(gè)命名屬性賦值如果在賦值之前對(duì)象沒有一個(gè)對(duì)應(yīng)名稱的屬性在賦值之后我們會(huì)獲得一個(gè)*/objectRef.testNumber = 8;/* 從屬性上讀回這個(gè)值*/var v
7、al = objectRef.testNumber;/* 然后,val現(xiàn)在持有了個(gè)值為8,就是我們剛才賦給對(duì)象命名屬性的值。*/但是所有對(duì)象都可以有原型,并且原型也是對(duì)象,所以接著它們也可以有原型,原型又可以有原型,如此就形成了所謂的原型鏈。當(dāng)鏈條中的對(duì)象有一個(gè)值為null的原型時(shí),原型鏈終止。Object構(gòu)造器的默認(rèn)原型值為null,所以:var objectRef = new Object(); /創(chuàng)建一個(gè)通用的JavaScript對(duì)象。創(chuàng)建一個(gè)對(duì)象的原型為Ototype,這個(gè)原型自身的屬性為null。所以objectRef的原型鏈僅僅包含一個(gè)對(duì)象:Ot
8、otype。然而: /* 一個(gè)構(gòu)造函數(shù),用來創(chuàng)建MyObject1型的對(duì)象。*/function MyObject1(formalParameter)/* 給創(chuàng)建的對(duì)象賦一個(gè)名為testNumber的屬性并且將傳給構(gòu)造函數(shù)的第一個(gè)參數(shù)值賦給它*/ this.testNumber = formalParameter;/* 一個(gè)構(gòu)造函數(shù),用來創(chuàng)建MyObject2型的對(duì)象*/function MyObject2(formalParameter) /* 給創(chuàng)建的對(duì)象一個(gè)名為testString的屬性并且將傳給構(gòu)造函數(shù)的第一個(gè)參數(shù)值賦給它 */ this.testString = formalPara
9、meter;/* 下一步操作,把關(guān)聯(lián)到MyObject2實(shí)例上的默認(rèn)prototype 替換成MyObject1的實(shí)例,給MyObject1的構(gòu)造函數(shù)傳遞一個(gè)值8, 這樣它的testNumber屬性將會(huì)被設(shè)置為這個(gè)值:*/MyOtotype = new MyObject1( 8 );/* 最后,創(chuàng)建MyObject2的實(shí)例,然后將這個(gè)對(duì)象的引用賦值給變量objRef給構(gòu)造函數(shù)傳遞一個(gè)字符串作為第一個(gè)參數(shù)*/var objectRef = new MyObject2( "String_Value" );被變量objectRef引用的MyObject2實(shí)例擁
10、有一個(gè)原型鏈。鏈中的第一個(gè)對(duì)象是MyObject1的實(shí)例,他被創(chuàng)建并賦值給MyObject2構(gòu)造函數(shù)的prorotype屬性。MyObject1的實(shí)例有一個(gè)原型,此對(duì)象由(引擎)實(shí)現(xiàn)分配給函數(shù)MyObject1的prototype屬性。這個(gè)對(duì)象(譯者注:指分配給MyObject1的prototype屬性的對(duì)象)有一個(gè)原型,就是默認(rèn)Object的原型,對(duì)應(yīng)Ototype所引用的對(duì)象。Ototype是一個(gè)值為null的原型,所以原型鏈到這一點(diǎn)結(jié)束。當(dāng)一個(gè)屬性訪問器試圖從變量objectRef所引用的對(duì)象上讀取一個(gè)命名屬性時(shí),整個(gè)原型鏈都會(huì)加入處理過程。在簡單的情
11、況下: var val = objectRef.testString;objectRef所引用的MyObject2的實(shí)例有一個(gè)名為”testString”的屬性,所以它就是這個(gè)屬性的值,設(shè)置為”String_Value”,它被賦值給變量val。然而: var val = objectRef.testNumber;不能從MyObject2實(shí)例自身讀取命名屬性,因?yàn)樗鼪]有這個(gè)屬性,但是變量val的值被設(shè)置為8而不是undefined,因?yàn)樵趯?duì)象自身上查找對(duì)應(yīng)的屬性失敗之后,解釋器會(huì)檢查對(duì)象的原型。它的原型是MyObject1的實(shí)例,它被創(chuàng)建時(shí)有一個(gè)屬性名為”testNumber”,并且把值8賦給了
12、這個(gè)屬性,所以屬性訪問器算出的值為8。MyObject1和MyObject2都沒有定義toString方法,但是如果一個(gè)屬性訪問器試圖從objectRef上讀取toString屬性的值:var val = objectRef.toString;變量val被賦值為一個(gè)函數(shù)的引用。這個(gè)函數(shù)是Object.ptorotype的toString屬性,它被返回是因?yàn)闄z查了objectRef的原型,當(dāng)發(fā)現(xiàn)objectRef沒有”toString”屬性,而是引用一個(gè)對(duì)象,所以當(dāng)發(fā)現(xiàn)原型中也缺少此屬性時(shí),則繼續(xù)查找原型的原型。它的原型是Orotype,確實(shí)有一個(gè)toString方法,所以va
13、l就被賦值為一個(gè)引用,指向返回的函數(shù)對(duì)象。 最后:var val = objectRef.madeUpProperty;返回undefined,因?yàn)樘幚磉^程檢查了整個(gè)原型鏈,發(fā)現(xiàn)沒有哪個(gè)對(duì)象有一個(gè)名為"madeUpPeoperty"的屬性,它最終獲得了Ototype的值,這是一個(gè)null值,然后處理過程返回一個(gè)undefined。對(duì)命名屬性的讀取操作會(huì)返回第一個(gè)找到的值,這個(gè)值可以在對(duì)象上或者在它的原型鏈上。給對(duì)象的命名屬性賦值時(shí),如果對(duì)象上沒有對(duì)應(yīng)的屬性存在,將會(huì)在對(duì)象自身創(chuàng)建一個(gè)屬性。這意味著,如果一個(gè)值被分配為objectRef.testNumber
14、=3,一個(gè)”testNumber”屬性將會(huì)在MyObject2的實(shí)例自身上被創(chuàng)建,隨后所有讀取這個(gè)屬性的操作將會(huì)獲得在對(duì)象上設(shè)置的值。不再需要檢查原型鏈來解析屬性訪問器,但是MyObject1的實(shí)例,”testNumber”屬性被賦值為8不會(huì)變。objectRef對(duì)象上分配的對(duì)應(yīng)屬性遮蓋了其原型鏈上的對(duì)應(yīng)屬性。注意:ECMAScript為內(nèi)部Object類型定義了一個(gè)內(nèi)部prototype屬性。這個(gè)屬性不能直接通過腳本訪問,但是它是一個(gè)引用對(duì)象原型鏈的對(duì)象鏈,同時(shí)內(nèi)部prototype屬性在屬性訪問解析中被使用。存在一個(gè)公共的prototype屬性與內(nèi)部prototype屬性相關(guān)聯(lián),允許對(duì)它進(jìn)
15、行賦值、定義和操作。這兩者之間關(guān)系的細(xì)節(jié)在ECMA 262 (第三版)中有描述,這已經(jīng)超越了本文的討論范圍。 標(biāo)識(shí)符解析,執(zhí)行環(huán)境和作用域鏈執(zhí)行環(huán)境執(zhí)行環(huán)境是ECMSScript規(guī)范使用的一個(gè)抽象概念(ECMA 262 第三版),用來定義ECMAScript實(shí)現(xiàn)所必須的行為。規(guī)范中沒有提到關(guān)于執(zhí)行環(huán)境應(yīng)該如何實(shí)現(xiàn)的任何事情,但是執(zhí)行環(huán)境有關(guān)聯(lián)的屬性,它引用了規(guī)范所定義的結(jié)構(gòu),所以它們可以被設(shè)想(甚至實(shí)現(xiàn))為擁有屬性的對(duì)象,雖然不是公共屬性。所有JavaScript代碼都是在一個(gè)執(zhí)行環(huán)境中被執(zhí)行的。全局代碼(內(nèi)嵌代碼,一般作為js文件或者HTML頁面加載)在全局執(zhí)行環(huán)境中執(zhí)行,每次函數(shù)調(diào)用(可以
16、是構(gòu)造器)都有一個(gè)分配的執(zhí)行環(huán)境。用eval函數(shù)執(zhí)行的代碼也有一個(gè)獨(dú)特的執(zhí)行環(huán)境,但是因?yàn)閑val從來不會(huì)被JavaScript程序員經(jīng)常使用,所以這里不討論它。執(zhí)行環(huán)境的特定的細(xì)節(jié)可以在ECMA 262的10.2(第三版)節(jié)找到。當(dāng)一個(gè)JavaScript函數(shù)被調(diào)用的時(shí)候,它進(jìn)入一個(gè)執(zhí)行環(huán)境,如果其它函數(shù)被調(diào)用(或者在相同的函數(shù)上遞歸),一個(gè)新的執(zhí)行環(huán)境被創(chuàng)建,在函數(shù)調(diào)用過程中進(jìn)入此環(huán)境。當(dāng)這個(gè)被調(diào)用的函數(shù)返回之后會(huì)返回先前的執(zhí)行環(huán)境。這樣,運(yùn)行中的javascript代碼就形成了一個(gè)執(zhí)行環(huán)境棧。 當(dāng)一個(gè)執(zhí)行環(huán)境被創(chuàng)建時(shí),很多事情按照規(guī)定的順序發(fā)生。首先,在一個(gè)函數(shù)的執(zhí)行環(huán)境中,一個(gè)“活動(dòng)”
17、對(duì)象被創(chuàng)建?;顒?dòng)對(duì)象是另一個(gè)規(guī)范機(jī)制。它可以被看做一個(gè)對(duì)象,因?yàn)樗罱K擁有可訪問的命名屬性,但是它不是一個(gè)普通對(duì)象,因?yàn)樗鼪]有原型(prototype)(至少不是一個(gè)已定義的prototype),并且它不能直接被javascript代碼引用。為調(diào)用函數(shù)創(chuàng)建執(zhí)行環(huán)境的下一步是創(chuàng)建一個(gè)arguments對(duì)象,這是一個(gè)類似數(shù)組的對(duì)象,擁有以整數(shù)值為下標(biāo)的成員,它和調(diào)用函數(shù)時(shí)傳遞的參數(shù)按次序?qū)?yīng)。arguments對(duì)象還有l(wèi)ength和callee兩個(gè)屬性(和這里的討論無關(guān),參見細(xì)節(jié)描述)?;顒?dòng)對(duì)象會(huì)被創(chuàng)建一個(gè)名為”arguments”的屬性,它會(huì)被賦值為一個(gè)引用,指向arguments對(duì)象。然后為執(zhí)
18、行環(huán)境分配一個(gè)作用域。作用域由一組對(duì)象列表(或鏈)組成。每個(gè)函數(shù)對(duì)象都有一個(gè)內(nèi)部scope屬性(稍后我們將涉及更多細(xì)節(jié)),它也由一組對(duì)象列表(或鏈)組成。調(diào)用函數(shù)時(shí)分配給執(zhí)行環(huán)境的作用域(scope)由一個(gè)列表組成,這個(gè)列表就是被對(duì)應(yīng)函數(shù)對(duì)象的scope屬性所引用的列表,并且把活動(dòng)對(duì)象添加到鏈(或者列表頂端)的前端而組成。然后,使用一個(gè)ECMA262所指的”可變”(Variable)對(duì)象執(zhí)行“變量初始化”的過程。然而,活動(dòng)對(duì)象被用作可變對(duì)象(注意,很重要:它們是同一個(gè)對(duì)象)??勺儗?duì)象的命名參數(shù)是為函數(shù)的每一個(gè)形參創(chuàng)建的,并且,如果調(diào)用函數(shù)的參數(shù)與這些參數(shù)對(duì)應(yīng),這些參數(shù)的值會(huì)被賦到屬性上(否則,
19、賦值為undefined)。內(nèi)部函數(shù)定義也被用來創(chuàng)建函數(shù)對(duì)象,它們也被設(shè)置到可變對(duì)象的屬性上,屬性名和定義函數(shù)時(shí)的函數(shù)名對(duì)應(yīng)。變量初始化的最后一步是創(chuàng)建可變對(duì)象的命名屬性,對(duì)應(yīng)函數(shù)中聲明的所有局部變量。 在可變對(duì)象上創(chuàng)建的,與申明的局部變量對(duì)應(yīng)的屬性,在變量初始化時(shí)被初始為undefined,局部變量的初始化實(shí)際不會(huì)發(fā)生,直到執(zhí)行函數(shù)體中代碼對(duì)相應(yīng)的表達(dá)式時(shí)才進(jìn)行計(jì)算。事實(shí)上,擁有arguments屬性的“活動(dòng)對(duì)象”,和擁有對(duì)應(yīng)到函數(shù)局部變量的命名屬性的“可變對(duì)象”,是同一個(gè)對(duì)象,這就允許把a(bǔ)rguments標(biāo)識(shí)符當(dāng)作函數(shù)的局部變量看待。最后,為使用this關(guān)鍵字分配一個(gè)值。如果分配的值指向一
20、個(gè)對(duì)象,那么使用this關(guān)鍵為前綴的屬性訪問器將會(huì)引用這個(gè)對(duì)象的屬性。如果分配(內(nèi)部)的值為null,那么this關(guān)鍵字將會(huì)指向全局對(duì)象。全局執(zhí)行環(huán)境做了一些細(xì)微不同的處理,因?yàn)樗鼪]有參數(shù),所以它沒有必要定義活動(dòng)對(duì)象去引用它們。全局執(zhí)行環(huán)境確實(shí)需要一個(gè)作用域,并且它的作用域鏈僅僅由一個(gè)對(duì)象構(gòu)成,就是全局對(duì)象。全局執(zhí)行環(huán)境也需要經(jīng)歷變量初始化,它的內(nèi)部函數(shù)是所聲明的普通頂級(jí)函數(shù),它們組成了javascript代碼的絕大部分。全局對(duì)象被用作可變對(duì)象,這就是為什么聲明的全局函數(shù)成為了全局對(duì)象的屬性。全局范圍內(nèi)聲明的變量也一樣。全局執(zhí)行環(huán)境也使用一個(gè)到全局對(duì)象的引用作為this對(duì)象。作用域鏈和scop
21、e調(diào)用函數(shù)時(shí)執(zhí)行環(huán)境中的作用域鏈,是通過把執(zhí)行環(huán)境中的活動(dòng)對(duì)象/可變對(duì)象添加到作用域鏈頂部而構(gòu)成,作用域鏈由函數(shù)對(duì)象的scope屬性持有,所以,理解scope屬性是如何定義的非常重要。 在ECMAScript中,函數(shù)是對(duì)象,它們?cè)谧兞砍跏蓟瘯r(shí)被創(chuàng)建:函數(shù)申明、執(zhí)行函數(shù)表達(dá)式或者通過調(diào)用Function構(gòu)造器。使用Function構(gòu)造器創(chuàng)建的函數(shù)對(duì)象,總是把scope屬性指向一個(gè)作用域鏈,其中僅僅包含全局對(duì)象。 通過函數(shù)聲明或者函數(shù)表達(dá)式創(chuàng)建的函數(shù)對(duì)象,擁有一個(gè)執(zhí)行環(huán)境中的作用域鏈,被賦值給它們內(nèi)部的scope屬性,它們是在這個(gè)執(zhí)行環(huán)境中被創(chuàng)建的。全局函數(shù)聲明最簡單的情況如: function
22、exampleFunction(formalParameter) . / function body code在為全局執(zhí)行環(huán)境進(jìn)行變量初始化時(shí),對(duì)應(yīng)的函數(shù)對(duì)象被創(chuàng)建。全局執(zhí)行環(huán)境有一個(gè)作用域鏈,它僅僅由全局對(duì)象組成。這樣,這個(gè)被創(chuàng)建的函數(shù)對(duì)象被全局對(duì)象以”exampleFunction”為名稱引用,并且被分配了一個(gè)內(nèi)部scope屬性,它引用一個(gè)作用域鏈,其中只包含全局對(duì)象。當(dāng)一個(gè)函數(shù)表達(dá)式在全局環(huán)境中被執(zhí)行時(shí),分配一個(gè)類似的作用域鏈: var exampleFuncRef = function() . / function body code 在這種情況下,在為全局執(zhí)行環(huán)境進(jìn)行變量初始化的過程
23、中,全局對(duì)象的一個(gè)命名屬性被創(chuàng)建,并且它的一個(gè)引用被賦值給全局對(duì)象的一個(gè)命名屬性,但是函數(shù)對(duì)象沒有被創(chuàng)建,直到對(duì)賦值表達(dá)式求值為止。但是在全局環(huán)境中創(chuàng)建函數(shù)對(duì)象的操作還是發(fā)生了,所以創(chuàng)建的函數(shù)對(duì)象的scope屬性仍然指向了只包含一個(gè)全局對(duì)象的作用域鏈。內(nèi)部函數(shù)聲明和以函數(shù)對(duì)象為結(jié)果的表達(dá)式在函數(shù)內(nèi)部的執(zhí)行環(huán)境中被創(chuàng)建,所以它們擁有更精細(xì)的作用域鏈。考慮以下代碼,定義了一個(gè)有內(nèi)部函數(shù)聲明的函數(shù),然后執(zhí)行外部函數(shù): function exampleOuterFunction(formalParameter) function exampleInnerFuncitonDec() . / inner
24、function body . / the rest of the outer function body.exampleOuterFunction( 5 );對(duì)應(yīng)著外部函數(shù)聲明的函數(shù)對(duì)象在全局執(zhí)行環(huán)境的變量初始化過程中被創(chuàng)建,所以它的scope屬性包含只有一個(gè)元素的作用域鏈,其中只有一個(gè)全局對(duì)象。當(dāng)外部代碼調(diào)用exampleOuterFunction時(shí),會(huì)為這個(gè)函數(shù)調(diào)用創(chuàng)建一個(gè)新的執(zhí)行環(huán)境,同時(shí)還有和它一起的活動(dòng)對(duì)象/可變對(duì)象。新執(zhí)行環(huán)境中的作用域構(gòu)成變成了:新的活動(dòng)對(duì)象加上外部函數(shù)對(duì)象scope屬性(僅包含全劇對(duì)象)所引用的作用域鏈。為新執(zhí)行環(huán)境進(jìn)行變量初始化導(dǎo)致創(chuàng)建了一個(gè)函數(shù)對(duì)象,它對(duì)應(yīng)
25、內(nèi)部函數(shù)定義,并且這個(gè)函數(shù)對(duì)象的scope屬性被分配為它被創(chuàng)建時(shí)執(zhí)行環(huán)境中的作用域值。作用域鏈包含了活動(dòng)對(duì)象,緊接著是全局對(duì)象。 到目前為止,這些都是自動(dòng)完成的,并且由構(gòu)建和執(zhí)行源碼(的機(jī)制)控制。執(zhí)行環(huán)境中的作用域鏈定義了所創(chuàng)建的函數(shù)對(duì)象的scope屬性,同時(shí)函數(shù)對(duì)象的scope屬性為它們的執(zhí)行環(huán)境定義了作用域(和對(duì)應(yīng)的活動(dòng)對(duì)象一起)。但是ECMAScript提供了with語句用來作為修改作用域鏈的手段。with語句對(duì)一個(gè)表達(dá)式求值,如果這個(gè)表達(dá)式是一個(gè)對(duì)象,它將會(huì)被添加到當(dāng)前執(zhí)行環(huán)境的作用域鏈中(在活動(dòng)對(duì)象/可變對(duì)象前面)。with語句然后執(zhí)行其它語句(這本身可能是一個(gè)語句塊)然后恢復(fù)執(zhí)行
26、環(huán)境的作用域鏈為之前的值。 一個(gè)函數(shù)聲明不會(huì)被with語句影響,因?yàn)樗鼈儠?huì)在變量初始化的時(shí)候?qū)е聞?chuàng)建函數(shù)對(duì)象,但是一個(gè)函數(shù)表達(dá)式可以在一個(gè)with段中被執(zhí)行:/* 創(chuàng)建一個(gè)全局變量y,引用一個(gè)對(duì)象*/var y = x:5; /對(duì)象字面值,有一個(gè)x屬性function exampleFuncWith() var z;/* 將變量y引用的對(duì)象添加到作用域鏈的最前端 */ with(y) /* 對(duì)函數(shù)表達(dá)式求值創(chuàng)建一個(gè)函數(shù)對(duì)象,將這個(gè)函數(shù)對(duì)象的引用賦值給局部變量z */ z = function() . / inner function expression body; . /* 執(zhí)行exampl
27、eFuncWith 函數(shù)*/exampleFuncWith();當(dāng)exampleFuncWith函數(shù)被調(diào)用時(shí),結(jié)果執(zhí)行環(huán)境擁有一個(gè)作用域鏈,由它的活動(dòng)對(duì)象加上全局對(duì)象組成。with語句執(zhí)行,當(dāng)函數(shù)表達(dá)式被求值時(shí),全局變量y引用的對(duì)象被添加到這個(gè)作用域鏈前面。由執(zhí)行函數(shù)表達(dá)式創(chuàng)建的函數(shù)對(duì)象被分配了一個(gè)scope屬性,它對(duì)應(yīng)于被創(chuàng)建時(shí)執(zhí)行環(huán)境的作用域。這個(gè)作用域鏈由對(duì)象y加上調(diào)用外部函數(shù)時(shí)執(zhí)行環(huán)境中的活動(dòng)對(duì)象,再加上全局對(duì)象組成。當(dāng)with語句中的代碼塊結(jié)束時(shí),執(zhí)行環(huán)境的作用域被恢復(fù)(y對(duì)象被刪除),但是函數(shù)對(duì)象已經(jīng)在那一點(diǎn)被創(chuàng)建,并且它的scope屬性被賦值成了一個(gè)作用域鏈的引用,它以y對(duì)象開頭
28、。標(biāo)識(shí)符解析標(biāo)識(shí)符根據(jù)作用域鏈解析。ECMA 262把this當(dāng)作了一個(gè)關(guān)鍵字而不是標(biāo)識(shí)符,這是不無道理的,因?yàn)樗偸歉鶕?jù)它被使用時(shí)執(zhí)行環(huán)境中的this值解析,而沒有引用作用域鏈。標(biāo)識(shí)符解析從作用域鏈的第一個(gè)對(duì)象開始。它被檢查,用來看看是否有與屬性名稱對(duì)應(yīng)標(biāo)識(shí)符。因?yàn)樽饔糜蜴準(zhǔn)且粋€(gè)對(duì)象鏈,這個(gè)檢查包含該對(duì)象的原型鏈(如果有)。如果在作用域鏈中的第一個(gè)對(duì)象上沒有找到對(duì)應(yīng)的值,查找過程在下一個(gè)對(duì)象上進(jìn)行。如此等等,直到作用域鏈中(或者其原型)的一個(gè)對(duì)象擁有一個(gè)屬性的名字與標(biāo)識(shí)符對(duì)應(yīng),或者作用域鏈耗盡為止。在標(biāo)識(shí)符上進(jìn)行的操作與以上描述的訪問對(duì)象原型的方式一樣。在作用域鏈中確定的擁有對(duì)應(yīng)屬性的對(duì)象,
29、會(huì)替代屬性訪問器中的對(duì)象,并且使用標(biāo)識(shí)符作為這個(gè)對(duì)象的屬性名。全局對(duì)象總是位于作用域鏈的最后。因?yàn)榻壎ǖ秸{(diào)用函數(shù)時(shí)的執(zhí)行環(huán)境將會(huì)擁有活動(dòng)/可變對(duì)象位于鏈的頂部,函數(shù)體中使用的標(biāo)識(shí)符將會(huì)首先做有效性檢測,看看它們是否與形參、內(nèi)部函數(shù)聲明的名稱或者局部變量相一致。這些將會(huì)被解析成活動(dòng)對(duì)象/可變對(duì)象的命名屬性。閉包自動(dòng)垃圾收集ECMAScript使用自動(dòng)垃圾收集。規(guī)范沒有規(guī)定細(xì)節(jié),把它留給具體實(shí)現(xiàn)去考慮,一些已知的實(shí)現(xiàn)給它們的垃圾收集操作一個(gè)很低的優(yōu)先級(jí)。但是總的思路是,如果一個(gè)對(duì)象變成孤立的(有沒留下到它的引用供執(zhí)行的代碼訪問),它就變成了一個(gè)需要進(jìn)行垃圾收集的變量,在未來的某個(gè)時(shí)間點(diǎn)它會(huì)被銷毀,
30、并且它所消耗的所有資源都會(huì)被釋放并歸還給系統(tǒng),以便重復(fù)利用。這通常是退出執(zhí)行環(huán)境時(shí)反生的情況。作用域鏈、活動(dòng)/可變對(duì)象以及所有在執(zhí)行環(huán)境中創(chuàng)建的對(duì)象,包括函數(shù)對(duì)象,將不再可以訪問并且將會(huì)變成可被垃圾收集的變量。構(gòu)造閉包通過返回一個(gè)函數(shù)對(duì)象,它在一次函數(shù)調(diào)用的執(zhí)行環(huán)境中被創(chuàng)建,從這次函數(shù)調(diào)用中,把這個(gè)內(nèi)部函數(shù)的引用賦值給另一個(gè)對(duì)象的屬性,或者直接把這個(gè)函數(shù)對(duì)象的引用分配給,例如一個(gè)全局變量、一個(gè)全局可見對(duì)象的屬性,或者賦給在調(diào)用外部函數(shù)時(shí)作為參數(shù)傳遞的對(duì)象引用。例如: function exampleClosureForm(arg1, arg2) var localVar = 8; functi
31、on exampleReturned(innerArg) return (arg1 + arg2)/(innerArg + localVar); /* 返回一個(gè)定義為exampleReturned的內(nèi)部函數(shù)引用 */ return exampleReturned;var globalVar = exampleClosureForm(2, 4);現(xiàn)在,調(diào)用exampleClosureForm函數(shù)時(shí)執(zhí)行環(huán)境中創(chuàng)建的函數(shù)對(duì)象不能被垃圾收集,因?yàn)樗蝗肿兞克貌⑶胰匀豢梢栽L問,它甚至可以使用globalVar(n)這種方式來執(zhí)行。因?yàn)楹瘮?shù)對(duì)象現(xiàn)在被globalVar引用,事情變得有一點(diǎn)小復(fù)雜,這個(gè)
32、函數(shù)創(chuàng)建了一個(gè)scope屬性,引用了一個(gè)屬于執(zhí)行環(huán)境的作用域鏈,包含了活動(dòng)/可變對(duì)象(還有全局對(duì)象),這個(gè)函數(shù)是在執(zhí)行環(huán)境中被創(chuàng)建的。現(xiàn)在,活動(dòng)/可變對(duì)象也不能被垃圾收集,因?yàn)閷?duì)象被globalVar引用,函數(shù)執(zhí)行時(shí)將會(huì)需要從它的scope屬性中把整個(gè)作用域鏈添加到調(diào)用它時(shí)所創(chuàng)建執(zhí)行環(huán)境的作用域中去。一個(gè)閉包形成了。內(nèi)部函數(shù)對(duì)象擁有任意變量和活動(dòng)/可變對(duì)象,這些對(duì)象由環(huán)境綁定到函數(shù)的作用域鏈上?;顒?dòng)/可變對(duì)象現(xiàn)在被限制,并被作用域鏈引用,作用域鏈被賦值給函數(shù)對(duì)象內(nèi)部的scope屬性,函數(shù)對(duì)象被變量globalVar引用?;顒?dòng)/可變對(duì)象和它的狀態(tài)及其屬性一起被保存。調(diào)用內(nèi)部函數(shù)時(shí),執(zhí)行環(huán)境中的作
33、用域解析將會(huì)解析標(biāo)識(shí)符,活動(dòng)/可變對(duì)象相應(yīng)的命名屬性就是這個(gè)對(duì)象的屬性。這些屬性值仍然可以讀寫,即使創(chuàng)建它們的執(zhí)行環(huán)境已經(jīng)退出。在以上例子中,在外部函數(shù)返回之后(退出它的執(zhí)行環(huán)境),活動(dòng)/可變對(duì)象有一個(gè)表示形參值、內(nèi)部函數(shù)定義和局部變量的狀態(tài)。arg1屬性值為2,arg2屬性值為4,loclaVar值為8,exampleReturned屬性是從外部函數(shù)返回的一個(gè)指向內(nèi)部函數(shù)對(duì)象的引用(為了方便,在后面的討論中我們將會(huì)把這個(gè)活動(dòng)/可變對(duì)象稱作”ActOuter1”)。如果exampleClousureForm函數(shù)再次被調(diào)用作: var secondGlobalVar = exampleClosu
34、reForm(12, 3);一個(gè)新的執(zhí)行環(huán)境將會(huì)被創(chuàng)建,還有一個(gè)新的活動(dòng)對(duì)象。然后一個(gè)新的函數(shù)對(duì)象將會(huì)被返回,擁有它自己唯一的scope屬性,引用一個(gè)作用域鏈,包含來自第二個(gè)執(zhí)行環(huán)境的活動(dòng)對(duì)象,arg1為12,arg2為3(為了方便,在后面的討論中我們將會(huì)把這個(gè)活動(dòng)/可變對(duì)象稱作”ActOuter2)通過再次執(zhí)行exampleClosureForm,形成了第二個(gè)不同的閉包。通過執(zhí)行exampleClosureForm函數(shù)創(chuàng)建了兩個(gè)函數(shù)對(duì)象,它們的引用被分別賦值給了全局變量globalVar和secondGlobalVar,函數(shù)對(duì)象返回表達(dá)式(arg1 + arg2)/(innerArg + l
35、ocalVar)。此表達(dá)式在四個(gè)標(biāo)識(shí)符上進(jìn)行了大量操作。這些標(biāo)識(shí)符如何被解析對(duì)閉包的用法和價(jià)值非常重要。考慮使用globalVar(2)的形式執(zhí)行g(shù)lobalVar所引用的函數(shù)對(duì)象。一個(gè)新的執(zhí)行環(huán)境被創(chuàng)建,還有一個(gè)活動(dòng)對(duì)象(我們將會(huì)稱它做”ActInner1”),它將被添加到作用域鏈頂部,被所執(zhí)行的函數(shù)對(duì)象中的scope屬性引用。ActInner1被賦給了一個(gè)名為innerArg的屬性,然后它的形參和參數(shù)值2被分配給它。新的執(zhí)行環(huán)境的作用域鏈目前是:ActInner1->ActOuter1->global object。根據(jù)作用域鏈,標(biāo)識(shí)符解析已經(jīng)完成,所以為了返回表達(dá)式(arg1
36、 + arg2)/(innerArg + localVar)的值,通過查找與標(biāo)識(shí)符名稱一致的屬性,這些標(biāo)識(shí)符的值將會(huì)被計(jì)算,在作用域鏈中的每個(gè)對(duì)象上依次進(jìn)行。作用域鏈中的第一個(gè)對(duì)象就是ActInner1,它有一個(gè)名為innerArg的屬性其,值為2。其它3個(gè)標(biāo)識(shí)符都與ActOuter1的命名屬性相對(duì)應(yīng);arg1為2,arg2為4,localVar為8。函數(shù)調(diào)用將會(huì)返回(2+4)/(2+8)。與被secondGlobalVar引用的其它相同函數(shù)對(duì)象相比較,secondGlobalVar(5)。把這個(gè)新執(zhí)行環(huán)境中的活動(dòng)對(duì)象叫做”ActInner2”,作用域鏈變成了:ActInner2->Ac
37、tOuter2->global object。ActInner2返回innerArg為5,ActOuer2返回arg1,arg2,localVar分別12,3,8。最終返回的值為(12 + 3)/(5 + 8).再次執(zhí)行secondGlobalVar,一個(gè)新的活動(dòng)對(duì)象出現(xiàn)在作用域鏈的頭部,但是ActOuter2仍然是作用域鏈中的下一個(gè)對(duì)象,并且它的命名屬性在解析標(biāo)識(shí)符arg1,arg2和localVar時(shí)會(huì)被再次使用。這就是ECMAScript內(nèi)部函數(shù)如何獲取、保持、訪問它被創(chuàng)建時(shí)的那個(gè)執(zhí)行環(huán)境中的形參、聲明的內(nèi)部函數(shù)和變量的方式。并且就是閉包如何允許這些函數(shù)對(duì)象以何種形式保持到這些值的
38、引用、讀寫它們,只要它繼續(xù)存在。所創(chuàng)建內(nèi)部函數(shù)執(zhí)行環(huán)境中的活動(dòng)/可變對(duì)象,存在于作用域鏈中,被函數(shù)對(duì)象的scope屬性引用,直到所有對(duì)內(nèi)部函數(shù)的引用都被釋放并且變?yōu)榭梢赃M(jìn)行垃圾收集為止(與它的作用域鏈中所有無效對(duì)象一起)。內(nèi)部函數(shù)自己也可以有內(nèi)部函數(shù),從函數(shù)的執(zhí)行環(huán)境中返回的內(nèi)部函數(shù)形成的閉包自己又可以返回內(nèi)部函數(shù),并形成它自己的閉包。每層嵌套,執(zhí)行環(huán)境都會(huì)獲得額外的活動(dòng)對(duì)象,此對(duì)象來自函數(shù)對(duì)象被創(chuàng)建的執(zhí)行環(huán)境。ECMAScript規(guī)范要求作用域鏈?zhǔn)怯邢薜?,但是沒有限定其長度。具體實(shí)現(xiàn)可以做一些實(shí)際的額外限制,但是具體的幅度還沒有相關(guān)報(bào)告。迄今為止,內(nèi)部函數(shù)的潛力已經(jīng)超越了任何實(shí)現(xiàn)它們的人的期
39、望。我們可以用閉包做什么?奇怪的是,這個(gè)問題的答案是任何東西,一切。有人告訴我,閉包使得ECMAScript可以模擬任何東西,所以唯一的限制就是想象力和模擬實(shí)現(xiàn)的能力了。這有一點(diǎn)深?yuàn)W,以一些更實(shí)際的東西開始可能更好。例1:使用函數(shù)引setTimeout閉包的一個(gè)常見用途是在執(zhí)行函數(shù)之前為函數(shù)提供參數(shù)。例如,把函數(shù)用作setTimeout函數(shù)的第一個(gè)參數(shù)在web瀏覽器環(huán)境很常用。setTimout計(jì)劃調(diào)用一個(gè)函數(shù)(或者一個(gè)javasctipt源碼字符串,但是不在當(dāng)前上下文中),作為其第一個(gè)參數(shù)提供,后面是一個(gè)以毫秒為單位的時(shí)間間隔(作為其第二個(gè)參數(shù))。如果一段代碼想使用setTimeout,它調(diào)
40、用setTimeout函數(shù)并傳遞一個(gè)函數(shù)對(duì)象的引用作為第一個(gè)參數(shù),和一個(gè)毫秒時(shí)間間隔作為第二個(gè)參數(shù),但是函數(shù)對(duì)象的引用無法為這個(gè)計(jì)劃執(zhí)行的函數(shù)提供參數(shù)。但是,代碼可以調(diào)用另一個(gè)函數(shù),它返回一個(gè)內(nèi)部函數(shù)的引用,把這個(gè)內(nèi)部函數(shù)的引用傳遞給setTimeout函數(shù)。執(zhí)行內(nèi)部函數(shù)需要使用的參數(shù)在調(diào)用(外部)函數(shù)時(shí)傳遞,這個(gè)(外部)函數(shù)生成了內(nèi)部函數(shù)。setTimeout執(zhí)行這個(gè)內(nèi)部函數(shù)而不傳遞參數(shù),但是內(nèi)部函數(shù)仍然可以訪問調(diào)用外部函數(shù)時(shí)所提供的參數(shù),外部函數(shù)返回了它(指內(nèi)部函數(shù)):function callLater(paramA, paramB, paramC)/* 返回一個(gè)匿名內(nèi)部函數(shù),它是通過一
41、個(gè)函數(shù)表達(dá)式創(chuàng)建的 */ return (function() /* 這個(gè)內(nèi)部函數(shù)將會(huì)被setTimeout執(zhí)行,并且當(dāng)它被執(zhí)行時(shí)它可以讀取并操縱傳遞給外部函數(shù)的參數(shù) */ paramAparamB = paramC; );./* 調(diào)用這個(gè)函數(shù),它將返回一個(gè)內(nèi)部函數(shù)對(duì)象的引用,這個(gè)函數(shù)對(duì)象是在它的執(zhí)行環(huán)境中被創(chuàng)建的 。傳遞參數(shù)傳遞給外部函數(shù),這些參數(shù)是最終執(zhí)行內(nèi)部函數(shù)時(shí)需要的。*/var functRef = callLater(elStyle, "display", "none");/* 調(diào)用setTimeout函數(shù),將賦值給functRef的內(nèi)部函數(shù)
42、引用作為第一個(gè)參數(shù)傳遞*/hideMenu=setTimeout(functRef, 500);例2:將函數(shù)與對(duì)象實(shí)例方法關(guān)聯(lián)有很多其它情況,當(dāng)一個(gè)函數(shù)的引用被賦值,然后在未來某時(shí)被執(zhí)行,為執(zhí)行這個(gè)函數(shù)提供參數(shù)非常有用,并且在執(zhí)行時(shí)提供不是非常合適,但是直到賦值時(shí)都無法確知。一個(gè)例子可能是,javascript對(duì)象被設(shè)計(jì)用來包裝與特定DOM元素的交互。它有doOnClick,doMouseOver和doMouseOut幾個(gè)方法,需要在DOM元素觸發(fā)對(duì)應(yīng)的事件時(shí)執(zhí)行,但是可能有任意數(shù)量的javascript對(duì)象實(shí)例被創(chuàng)建并關(guān)聯(lián)到不同DOM元素,并且單個(gè)對(duì)象實(shí)例并不知道它們將會(huì)如何受雇于實(shí)例化它們
43、的代碼。對(duì)象實(shí)例不知道如何全局地引用它們自己,因?yàn)樗鼈儾恢浪鼈兊膶?shí)例會(huì)被賦值給哪個(gè)全局變量(如果存在)。所以問題是,執(zhí)行一個(gè)事件處理函數(shù),它關(guān)聯(lián)了一個(gè)特定的javascript對(duì)象,并知道調(diào)用這個(gè)對(duì)象的哪個(gè)方法。以下例子使用了一個(gè)一般性的基于閉包的小函數(shù),它把事件處理函數(shù)綁定到對(duì)象實(shí)例。安排事件處理程序調(diào)用對(duì)象實(shí)例的特定方法,傳遞事件對(duì)象和一個(gè)所關(guān)聯(lián)元素的引用給對(duì)象的方法,并返回該方法的返回值。/* 一個(gè)通用函數(shù),將一個(gè)對(duì)象關(guān)聯(lián)到事件處理器。返回的內(nèi)部函數(shù)將被用作事件處理器。對(duì)象實(shí)例以名為obj的參數(shù)傳遞對(duì)象上需要被調(diào)用方法的名字以名為methodName(字符串)的參數(shù)傳遞*/functi
44、on associateObjWithEvent(obj, methodName)/* 返回的內(nèi)部函數(shù)將作為替代,扮演DOM元素事件處理器的角色 */ return (function(e) /*一般化的事件對(duì)象,在DOM標(biāo)準(zhǔn)的瀏覽器中,它將會(huì)被以名為e的參數(shù)傳遞,對(duì)于IE事件對(duì)象不會(huì)被作為參數(shù)傳遞給作為事件處理器的內(nèi)部函數(shù) */ e = e|window.event;/譯者注:這是兼容IE事件對(duì)象的一個(gè)重要技巧 /* 事件處理器調(diào)用obj對(duì)象上的一個(gè)方法,其名稱被字符串methodName持有傳遞一般化的事件以及元素的引用,事件處理器將會(huì)以this引用它們(這能起作用是因?yàn)檫@個(gè)內(nèi)部函數(shù)被作為
45、這個(gè)元素的一個(gè)方法執(zhí)行,因?yàn)樗呀?jīng)被作為一個(gè)事件處理函數(shù)賦值) */ return objmethodName(e, this); );/* 這個(gè)構(gòu)造函數(shù)創(chuàng)建對(duì)象,把它們自己關(guān)聯(lián)到DOM元素,這些元素的ID被作為字符串傳遞給構(gòu)造器。對(duì)象實(shí)例需要安排當(dāng)相應(yīng)元素觸發(fā)onclick,onmouseover,onmouseout事件時(shí),它們對(duì)象實(shí)例上的相應(yīng)方法會(huì)被調(diào)用*/function DhtmlObject(elementId)/* 一個(gè)函數(shù)被調(diào)用,用來獲取DOM元素的引用(如果沒有找到為null),傳遞所需元素的ID作為參數(shù)。返回值將會(huì)被賦給局部變量el */ var el = getEleme
46、ntWithId(elementId);/* 因?yàn)閕f語句,el的值將會(huì)被隱含轉(zhuǎn)換成布爾型,所以如果el引用一個(gè)對(duì)象結(jié)果將會(huì)為true,如果el為null則結(jié)果為false。所以,接下來的代碼塊只有在el變量引用一個(gè)DOM元素時(shí)才會(huì)被執(zhí)行 */ if(el) /* 為了給元素的事件處理器賦一個(gè)值,該對(duì)象調(diào)用associateObjWithEvent函數(shù),指定自己(使用this關(guān)鍵字)作為需要在其中調(diào)用方法的對(duì)象,并且提供需要調(diào)用方法的名稱。associateObjWithEvent函數(shù)將會(huì)返回一個(gè)內(nèi)部函數(shù)引用,它將會(huì)被賦值給DOM元素的事件處理器。這個(gè)內(nèi)部函數(shù)將會(huì)調(diào)用javascript對(duì)象上
47、所需的方法響應(yīng)事件: */ el.onclick = associateObjWithEvent(this, "doOnClick"); el.onmouseover = associateObjWithEvent(this, "doMouseOver"); el.onmouseout = associateObjWithEvent(this, "doMouseOut"); . . / doOnClick method body. . / doMouseOver method body. . / doMouseOut method b
48、ody.這樣DhtmlObject的所有實(shí)例都可以把自己關(guān)聯(lián)到自己感興趣的DOM元素上,而不需要知道它們自己是如何被其它代碼調(diào)用的任何細(xì)節(jié),以及對(duì)全局命名空間的影響、與其它DhtmlObject實(shí)例冒沖突的風(fēng)險(xiǎn)。 例3:封裝相關(guān)的功能一種方法是把數(shù)組做成一個(gè)全局變量,這樣它就可以被重復(fù)利用而無需重新創(chuàng)建。但是這樣做的后果是,除了全局變量所引用的函數(shù)會(huì)使用這個(gè)數(shù)組之外,還存在第二個(gè)全局變量引用數(shù)組自身。其影響就是降低代碼的受控程度,因?yàn)?,如果它被用在其它地方,它的作者可能不記得既?dǎo)入函數(shù)定義又導(dǎo)入數(shù)組定義。這也導(dǎo)致代碼不能與其它代碼方便地進(jìn)行交互,因?yàn)槌吮WC函數(shù)名在全局命名空間中唯一之外,還必
49、須保證它所依賴的數(shù)組在全局命名空間中名稱唯一。閉包允許把這個(gè)緩沖區(qū)數(shù)組綁定(并整齊地包裝)到依賴它的函數(shù)上,同時(shí)把緩沖區(qū)數(shù)組的屬性名保持在全局命名空間之外,從而避免了命名沖突和意外交互的風(fēng)險(xiǎn)。這里的技巧是創(chuàng)建一個(gè)額外的執(zhí)行環(huán)境,通過執(zhí)行一個(gè)內(nèi)聯(lián)的函數(shù)表達(dá)式,讓這個(gè)函數(shù)表達(dá)式返回一個(gè)內(nèi)部函數(shù),它將被外部代碼使用。這個(gè)操作只執(zhí)行一次,所以數(shù)組只會(huì)被創(chuàng)建一次,但是卻可以被依賴它的函數(shù)重復(fù)使用。以下代碼創(chuàng)建了一個(gè)會(huì)返回HTML字符串的函數(shù),其中大部分是常量,但是這些常量字符序列需要插入調(diào)用函數(shù)時(shí)所提供的變量信息。從對(duì)一個(gè)函數(shù)表達(dá)式的內(nèi)聯(lián)執(zhí)行,會(huì)返回一個(gè)內(nèi)部函數(shù)的引用,并且賦值給一個(gè)全局變量,這樣它就可
50、以被當(dāng)作全局函數(shù)調(diào)用。緩沖區(qū)數(shù)組定義為外部函數(shù)表達(dá)式的局部變量。它沒有被暴露在全局命名空間中,并且沒有必要被重復(fù)創(chuàng)建,無論使用它的函數(shù)何時(shí)被調(diào)用。/* 全局變量getImgInPositionedDivHtml被聲明并賦值為內(nèi)部函數(shù)表達(dá)式的引用,這個(gè)內(nèi)部函數(shù)是對(duì)外部函數(shù)表達(dá)式的一次調(diào)用產(chǎn)生的。這個(gè)內(nèi)部函數(shù)返回一個(gè)HTML字符串,描述了一個(gè)絕對(duì)定位的DIV,包裝在IMG元素的周圍,使得所有變量的屬性值都被以調(diào)用函數(shù)時(shí)的參數(shù)提供:*/var getImgInPositionedDivHtml = (function()/* buffAr數(shù)組被賦值給外部函數(shù)表達(dá)式的一個(gè)局部變量。它僅僅被創(chuàng)建一次,并
51、且這個(gè)數(shù)組實(shí)例對(duì)內(nèi)部函數(shù)有效,所以每次執(zhí)行這個(gè)內(nèi)部函數(shù)時(shí)它都有效??兆址?dāng)前被用來作占位符,它們將會(huì)被內(nèi)部函數(shù)插入到數(shù)組中(譯者注:指它們將會(huì)被實(shí)際值替換): */ var buffAr = '<div id="', '', /index 1, DIV ID attribute '" style="position:absolute;top:', '', /index 3, DIV top position 'px;left:', '', /index 5, D
52、IV left position 'px;width:', '', /index 7, DIV width 'px;height:', '', /index 9, DIV height 'px;overflow:hidden;"><img src="', '', /index 11, IMG URL '" width="', '', /index 13, IMG width '" height=&q
53、uot;', '', /index 15, IMG height '" alt="', '', /index 17, IMG alt text '"></div>' ;/* 返回內(nèi)部函數(shù)的引用,它是一個(gè)函數(shù)表達(dá)式執(zhí)行的結(jié)果。每次調(diào)用getImgInPositionedDivHtml( . )時(shí),這個(gè)內(nèi)部函數(shù)對(duì)象將會(huì)被調(diào)用: */ return (function(url, id, width, height, top, left, altText) /* 將各個(gè)參數(shù)賦值到緩沖
54、區(qū)數(shù)組中對(duì)應(yīng)的位置: */ buffAr1 = id; buffAr3 = top; buffAr5 = left; buffAr13 = (buffAr7 = width); buffAr15 = (buffAr9 = height); buffAr11 = url; buffAr17 = altText; /*把數(shù)組中的每個(gè)元素以空字符串連接到一起,返回這個(gè)字符串 (這和僅僅把元素連接到一起是一樣的): */ return buffAr.join(''); ); /內(nèi)部函數(shù)表達(dá)式結(jié)束)();/*外部函數(shù)表達(dá)式的內(nèi)聯(lián)調(diào)用*/如果一個(gè)函數(shù)依賴一個(gè)(或者多個(gè))其它函數(shù),并且這些其
55、它的函數(shù)不想直接受雇于任何其它代碼,那么可以使用同樣的技術(shù)把這些函數(shù)組織到需要被公開暴露的函數(shù)中。把一個(gè)復(fù)雜的多個(gè)函數(shù)處理的過程制作成便于攜帶的封裝代碼單元。其它例子閉包最著名的應(yīng)用之一可能是Douglas Crockford的在ECMAScript對(duì)象中模擬私有實(shí)例變量的技術(shù)。它可以被擴(kuò)展到各種基于作用域嵌套結(jié)構(gòu)的訪問控制/可見性控制中,包括為ECMAScript對(duì)象模擬私有靜態(tài)成員。閉包可能的應(yīng)用無窮無盡,理解它們是如何運(yùn)作的可能是認(rèn)識(shí)它們用途的最好指南。意外的閉包把任何函數(shù)渲染到在創(chuàng)建它們的函數(shù)體之外可以訪問會(huì)形成一個(gè)閉包。這導(dǎo)致閉包非常容易被創(chuàng)建,并且后果之一就是不熟悉閉包作為語言特性的j
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 石河子大學(xué)《應(yīng)急人力資源管理》2022-2023學(xué)年第一學(xué)期期末試卷
- 物業(yè)智能化解決方案
- 石河子大學(xué)《數(shù)學(xué)文化賞析》2021-2022學(xué)年第一學(xué)期期末試卷
- 石河子大學(xué)《口腔頜面外科學(xué)》2022-2023學(xué)年第一學(xué)期期末試卷
- 石河子大學(xué)《工程熱力學(xué)與傳熱學(xué)》2023-2024學(xué)年第一學(xué)期期末試卷
- 精神科新冠肺炎演練
- 沈陽理工大學(xué)《數(shù)學(xué)建?!?023-2024學(xué)年第一學(xué)期期末試卷
- 沈陽理工大學(xué)《液壓與氣動(dòng)技術(shù)》2022-2023學(xué)年第一學(xué)期期末試卷
- 沈陽理工大學(xué)《電氣控制與PC技術(shù)》2022-2023學(xué)年期末試卷
- 沈陽理工大學(xué)《場地設(shè)計(jì)》2021-2022學(xué)年第一學(xué)期期末試卷
- 2024繼續(xù)教育《醫(yī)學(xué)科研誠信與醫(yī)學(xué)了研究倫理》答案
- 《在政府教育工作督導(dǎo)評(píng)估反饋會(huì)上的表態(tài)發(fā)言》
- 安安全全坐火車PPT課件
- 交通事故責(zé)任劃分圖例
- 六年級(jí)上冊(cè)數(shù)學(xué)比的計(jì)算題
- 第三方破壞事故分析與對(duì)策
- 投標(biāo)保證金退付申請(qǐng)書四篇
- 鉆井常用計(jì)算公式
- 混凝土澆筑監(jiān)理旁站記錄(完整)
- 創(chuàng)傷的救治流程PPT課件
- 上公司財(cái)務(wù)風(fēng)險(xiǎn)分析與防范——以蘇寧云商為例
評(píng)論
0/150
提交評(píng)論