JavaScript中的閉包(Closure)詳細(xì)介紹
閉包是JavaScript中一個重要的特性,其最大的作用在于保存函數(shù)運(yùn)行過程中的信息。在JavaScript中,閉包的諸多特性源自函數(shù)調(diào)用過程中的作用域鏈上。
函數(shù)調(diào)用對象與變量的作用域鏈
對于JavaScript中的每一次函數(shù)調(diào)用,JavaScript都會創(chuàng)建一個局部對象以儲存在該函數(shù)中定義的局部變量;如果在該函數(shù)內(nèi)部還有一個嵌套定義的函數(shù)(nested function),那么JavaScript會在已經(jīng)定義的局部對象之上再定義一個嵌套局部對象。對于一個函數(shù),其內(nèi)部有多少層的嵌套函數(shù)定義,也就有多少層的嵌套局部對象。該局部對象稱為“函數(shù)調(diào)用對象”(ECMAScript 3中的“call object”,ECMAScript 5中改名為“declarative environment record”,但個人認(rèn)為還是ECMAScript 3中的名稱更容易理解一些)。以下面的函數(shù)調(diào)用為例:
function f(x){
var a = 10;
return a*x;
}
console.log(f(6));//60
在這個簡單的例子中,當(dāng)調(diào)用f()函數(shù)時,JavaScript會創(chuàng)建一個f()函數(shù)的調(diào)用對象(姑且稱之為f_invokeObj),在f_invokeObj對象內(nèi)部有兩個屬性:a和x;運(yùn)行f()時,a值為10而x值為6,因此最后的返回結(jié)果為60。圖示如下:
當(dāng)存在函數(shù)嵌套時,JavaScript將創(chuàng)建多個函數(shù)調(diào)用對象:
function f(x){
var a = 10;
return a*g(x);
function g(b){
return b*b;
}
}
console.log(f(6));//360
在這個例子中,當(dāng)調(diào)用f()函數(shù)時,JavaScript會創(chuàng)建一個f()函數(shù)的調(diào)用對象(f_invokeObj),其內(nèi)部有兩個屬性a和x,a值為10而x值為6;運(yùn)行f()時,JavaScript會對f()函數(shù)中的g()函數(shù)進(jìn)行解析定義,并創(chuàng)建g()的調(diào)用對象(g_invokeObj),其內(nèi)部有一個屬性b,b值與傳入?yún)?shù)x相同為6,因此最后的返回結(jié)果為360。圖示如下:
可以看到,函數(shù)調(diào)用對象形成了一條鏈。當(dāng)內(nèi)嵌函數(shù)g()運(yùn)行,需要獲取變量值的時候,會從最近的函數(shù)調(diào)用對象中開始進(jìn)行搜索,如果無法搜索到,則沿函數(shù)調(diào)用對象鏈在更遠(yuǎn)的調(diào)用對象中進(jìn)行搜尋,此即所謂的“變量的作用域鏈”。如果兩個函數(shù)調(diào)用對象中出現(xiàn)相同的變量,則函數(shù)會取離自己最近的那個調(diào)用對象中的變量值:
function f(x){
var a = 10;
return a*g(x);
function g(b){
var a = 1;
return b*b*a;
}
}
console.log(f(6));//360, not 3600
在上面的例子中,g()函數(shù)的調(diào)用對象(g_invokeObj)和f()函數(shù)的調(diào)用對象(f_invokeObj)中均存在變量a且a的值不同,當(dāng)運(yùn)行g(shù)()函數(shù)時,在g()函數(shù)內(nèi)部所使用的a值為1,而在g()函數(shù)外部所使用的a值則為10。圖示此時的函數(shù)調(diào)用對象鏈如下:
什么是閉包?
在JavaScript中所有的函數(shù)(function)都是對象,而定義函數(shù)時都會產(chǎn)生相應(yīng)的函數(shù)調(diào)用對象鏈,一次函數(shù)定義對應(yīng)一個函數(shù)調(diào)用對象鏈。只要函數(shù)對象存在,相應(yīng)的函數(shù)調(diào)用對象就存在;一旦某函數(shù)不再被使用,相應(yīng)的函數(shù)調(diào)用對象就會被垃圾回收掉;而這種函數(shù)對象和函數(shù)調(diào)用對象鏈之間的一一組合,就稱之為“閉包”。在上面f()函數(shù)和g()函數(shù)的例子中,就存在兩個閉包:f()函數(shù)對象和f_invokeObj對象組成了一個閉包,而g()函數(shù)對象和g_invokeObj-f_invokeObj對象鏈一起組成了第二個閉包。當(dāng)g()函數(shù)執(zhí)行完畢后,由于g()函數(shù)不再被使用,因此g()閉包被垃圾回收了;之后,當(dāng)f()函數(shù)執(zhí)行完畢后,由于同樣的原因,f()閉包也被垃圾回收了。
從閉包的定義可以得出結(jié)論:所有的JavaScript函數(shù)在定義后都是閉包 – 因為所有的函數(shù)都是對象,所有的函數(shù)在執(zhí)行后也都有其對應(yīng)的調(diào)用對象鏈。
不過,令閉包真正發(fā)揮作用的是嵌套函數(shù)的情況。由于內(nèi)嵌函數(shù)是在外部函數(shù)運(yùn)行的時候才開始定義的,因此內(nèi)嵌函數(shù)的閉包中所保存的變量值(尤其是外部函數(shù)的局部變量值)是這次運(yùn)行過程中的值。只要內(nèi)嵌函數(shù)對象依然存在,那么其閉包就依然存在(閉包中的變量值不會發(fā)生任何改變),從而也就實現(xiàn)了保存函數(shù)運(yùn)行過程的信息這個目的。考慮以下這個例子:
var a = "outside";
function f(){
var a = "inside";
function g(){return a;}
return g;
}
var result = f();
console.log(result());//inside
在這個例子中,當(dāng)運(yùn)行f()函數(shù)時,g()函數(shù)被定義,同時創(chuàng)建了g()函數(shù)的閉包,g()閉包包含了g_invokeObj-f_invokeObj對象鏈,因此保存了f()函數(shù)執(zhí)行過程中的變量a的值。當(dāng)執(zhí)行console.log()語句時,由于g函數(shù)對象仍然存在,因此g()閉包也依然存在;當(dāng)運(yùn)行這個仍然存在的g函數(shù)對象時,JavaScript會使用依然存在的g()閉包并從中獲取變量a的值(“inside”)。
- JavaScript 匿名函數(shù)(anonymous function)與閉包(closure)
- JavaScript 閉包深入理解(closure)
- 學(xué)習(xí)Javascript閉包(Closure)知識
- 深入理解JavaScript系列(16) 閉包(Closures)
- 理解javascript函數(shù)式編程中的閉包(closure)
- javascript閉包(Closure)用法實例簡析
- Javascript閉包(Closure)詳解
- JavaScript知識點(diǎn)總結(jié)(十六)之Javascript閉包(Closure)代碼詳解
- javascript中閉包(Closure)詳解
- javascript中閉包c(diǎn)losure的深入講解
相關(guān)文章
學(xué)習(xí)javascript面向?qū)ο?理解javascript對象
這篇文章主要介紹了javascript對象,學(xué)習(xí)javascript面向?qū)ο?,感興趣的小伙伴們可以參考一下2016-01-01Javascript異步編程async實現(xiàn)過程詳解
這篇文章主要介紹了Javascript異步編程async實現(xiàn)過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-04-04javascript 密碼強(qiáng)度驗證規(guī)則、打分、驗證(給出前端代碼,后端代碼可根據(jù)強(qiáng)度規(guī)則翻譯)
密碼強(qiáng)度是一個很普遍的功能,比較簡單,主要是怎么制定這個強(qiáng)度規(guī)則。2010-05-05javascript驗證內(nèi)容為數(shù)字以及長度為10的簡單實例
下面小編就為大家?guī)硪黄猨avascript驗證內(nèi)容為數(shù)字以及長度為10的簡單實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-08-08js中apply和Math.max()函數(shù)的問題及區(qū)別介紹
這篇文章主要介紹了js中apply和Math.max()函數(shù)的問題,本文給大家?guī)韮煞N答案,每一種答案給大家介紹的非常詳細(xì),在文章底部給大家提到了js中Math.max.apply和Math.max的區(qū)別,感興趣的朋友一起看看吧2018-03-03