JavaScript閉包與作用域鏈實(shí)例分析
本文實(shí)例講述了JavaScript閉包與作用域鏈。分享給大家供大家參考,具體如下:
閉包定義
閉包指的是有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù)。創(chuàng)建閉包的常見方式,就是在一個(gè)函數(shù)A內(nèi)部創(chuàng)建另一個(gè)函數(shù)B,那么函數(shù)B就是一個(gè)閉包,可以訪問函數(shù)A作用域中的所有變量。
JavaScript的閉包與作用域鏈密不可分,因此本文可以和JavaScript的作用域鏈相對(duì)照分析,一定可以對(duì)JavaScript的閉包和作用域鏈有更深的理解。
下面我們?nèi)匀灰詂reateComparisonFunction為例進(jìn)行閉包的分析。
//step1: define createComparisonFunction
function createComparisonFunction(propertyName){
return function(object1, object2){
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if (value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
};
}
//step2: call createComparisonFunction
var compareName = createComparisonFunction("name");
var compareAge = createComparisonFunction("age");
//step3: call compare
var object1 = {
name : "Nicholas",
age : 25
};
var object2 = {
name : "Greg",
age : 27
};
var result1 = compareName(object1, object2); // 1
var result2 = compareAge(object1, object2); // -1
//step4: dereference closure for recycle memory
compareName = null;
compareAge = null;
在這個(gè)例子中,匿名函數(shù)function(object1, object2)是一個(gè)閉包,能訪問createComparisonFunction作用域里的所有變量,自然也包含propertyName屬性, 因?yàn)閜ropertyName參數(shù)的不同,導(dǎo)致比較的屬性也有所不同,從而函數(shù)執(zhí)行結(jié)果也有不同。
閉包與變量
從JavaScript的作用域鏈中,我們了解到JavaScript是通過作用域鏈來確定函數(shù)執(zhí)行環(huán)境的作用域的,這種機(jī)制會(huì)引出一個(gè)值得注意的副作用,即閉包只能取得包含函數(shù)中任何變量的最后一個(gè)值。閉包是通過引用外部函數(shù)的活動(dòng)對(duì)象來訪問該活動(dòng)對(duì)象中的所有變量,因此在外部函數(shù)執(zhí)行過程中,這些變量的值可能會(huì)變化,但是在外部函數(shù)執(zhí)行完畢之后,外部函數(shù)的活動(dòng)對(duì)象便不會(huì)再改變,因此在執(zhí)行閉包的時(shí)候,閉包通過作用域鏈訪問到外部函數(shù)的活動(dòng)對(duì)象中的所有變量都只可能是在外部函數(shù)執(zhí)行完畢之后,外部函數(shù)的活動(dòng)對(duì)象中最后所保存的值。我們通過一個(gè)例子來說明這種副作用。
function createFunctions(){
var result = new Array();
for (var i = 0; i < 10; i++){
result[i] = function(){
return i;
};
}
return result;
}
var functions = createFunctions();
for(var i = 0; i < functions.length; i++){
console.log(functions[i]());
}
輸出的結(jié)果是
10 10 10 10 10 10 10 10 10 10
從表面上看,似乎每個(gè)函數(shù)都應(yīng)該返回自己的索引值,但實(shí)際上,每個(gè)函數(shù)都返回10。因?yàn)槊總€(gè)函數(shù)的作用域鏈中都保存著createFunctions函數(shù)的活動(dòng)對(duì)象,所以他們引用的都是這個(gè)createFunctions函數(shù)的活動(dòng)對(duì)象中的變量i,在createFunctions函數(shù)返回之后,變量i的值是10,此時(shí)每個(gè)函數(shù)都引用著保存變量i的同一個(gè)變量對(duì)象,所以每個(gè)函數(shù)內(nèi)部i的值都是10。
我們以調(diào)用functions[3]()為例,圖解一下:

Closure函數(shù)的Function對(duì)象的作用域鏈引用的createFunctions的活動(dòng)對(duì)象中保留的變量i的值為10。所以不管是functions[3]()還是functions[5](),其運(yùn)行時(shí)上下文的作用域鏈引用的createFunctions的活動(dòng)對(duì)象都是同一個(gè)活動(dòng)對(duì)象,該活動(dòng)對(duì)象中保留的變量i的值是10。
如何避免這種局面?我們可以通過創(chuàng)建另一個(gè)匿名函數(shù)讓閉包的行為符合預(yù)期。
function createFunctions(){
var result = new Array();
for (var i = 0; i < 10; i++){
result[i] = function(num){
return function(){
return num;
}
}(i);
}
return result;
}
var functions = createFunctions();
for(var i = 0; i < functions.length; i++){
console.log(functions[i]());
}
輸出的結(jié)果是
0 1 2 3 4 5 6 7 8 9
這個(gè)代碼片段與前面的代碼的區(qū)別在于立即調(diào)用了一個(gè)匿名函數(shù)function(num),使得閉包function()引用的是function(num)的活動(dòng)對(duì)象,訪問的是該活動(dòng)對(duì)象中的變量num而不是createFunctions活動(dòng)對(duì)象中的變量i,而在立即調(diào)用function(num)的num是索引值0,1,2…9。
我們?nèi)耘f以調(diào)用functions[3]()為例,圖解一下:

在執(zhí)行createFunctions函數(shù)的時(shí)候,會(huì)依次調(diào)用function(0), function(1) … function(9), 生成function(0), function(1) … function(9)這10個(gè)function(num)的活動(dòng)對(duì)象,而result[0], result[1] … result[9]這10個(gè)匿名函數(shù)對(duì)象的作用域鏈分別引用這10個(gè)function(num)的活動(dòng)對(duì)象,而其中的變量num的值也對(duì)應(yīng)的為0, 1 … 9。
所以不管是functions[3]()還是functions[5](),其運(yùn)行時(shí)上下文的作用域鏈都會(huì)引用在執(zhí)行createFunctions函數(shù)時(shí)候所執(zhí)行的function(3)或者function(5)這些function(num)函數(shù)的活動(dòng)對(duì)象,這些活動(dòng)對(duì)象都是不同的活動(dòng)對(duì)象,其中保留的num值分別為3, 5。
更多關(guān)于JavaScript相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《javascript面向?qū)ο笕腴T教程》、《JavaScript錯(cuò)誤與調(diào)試技巧總結(jié)》、《JavaScript數(shù)據(jù)結(jié)構(gòu)與算法技巧總結(jié)》、《JavaScript遍歷算法與技巧總結(jié)》及《JavaScript數(shù)學(xué)運(yùn)算用法總結(jié)》
希望本文所述對(duì)大家JavaScript程序設(shè)計(jì)有所幫助。
相關(guān)文章
JS小知識(shí)之如何將CSV轉(zhuǎn)換為JSON字符串
CSV文件一般是以逗號(hào)為分隔值的文件(Comma-Separated?Values,CSV,有時(shí)也稱為字符分隔值,因?yàn)榉指糇址部梢圆皇嵌禾?hào)),其文件以純文本形式存儲(chǔ)表格數(shù)據(jù)(數(shù)字和文本),下面這篇文章主要給大家介紹了JS小知識(shí)之如何將CSV轉(zhuǎn)換為JSON字符串的相關(guān)資料,需要的朋友可以參考下2023-06-06
JavaScript實(shí)現(xiàn)多維數(shù)組的方法
這篇文章主要介紹了JavaScript實(shí)現(xiàn)多維數(shù)組的方法,有需要的朋友可以參考一下2013-11-11
js實(shí)現(xiàn)定時(shí)進(jìn)度條完成后切換圖片
這篇文章主要介紹了js實(shí)現(xiàn)定時(shí)進(jìn)度條,進(jìn)度100%以后可以切換圖片,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01
JavaScript中實(shí)現(xiàn)跨標(biāo)簽頁通信的方法詳解
跨標(biāo)簽頁通信是指在瀏覽器中的不同標(biāo)簽頁之間進(jìn)行數(shù)據(jù)傳遞和通信的過程,這篇文章為大家介紹了一下常見的跨標(biāo)簽頁通信方式,感興趣的小伙伴可以了解下2023-11-11
es6中Promise 對(duì)象基本功能與用法實(shí)例分析
這篇文章主要介紹了es6中Promise 對(duì)象基本功能與用法,結(jié)合實(shí)例形式分析了es6中Promise對(duì)象的基本功能、用法及操作注意事項(xiàng),需要的朋友可以參考下2020-02-02

