閉包定義:有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù)
引子
function createFunctions(){
var result = new Array();
for(var i = 0; i < 10; i++){
result[i] = function(){
return i;
};
}
return result;
}
var arr = createFunction();
arr[0]();
arr[1]();
...
分析: 表面看悼粮,似乎每個(gè)數(shù)組都應(yīng)該返回自己的索引值,即位置0的函數(shù)返回0烙荷,位置1的函數(shù)返回1...产舞。但實(shí)際上是:每個(gè)數(shù)組的函數(shù)返回的都是10;這是因?yàn)椋寒?dāng)調(diào)用createFunction之后讨跟,arr[i]中每個(gè)函數(shù)引用的都是同一個(gè)變量i纪他。而此時(shí)變量i的值是10。
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 arr = createFunction();
arr[0]();
arr[1]();
分析:重寫了createFunction函數(shù)之后晾匠,每個(gè)函數(shù)返回的就是各自不同的索引值了茶袒。在這個(gè)版本中,我們沒有直接把閉包賦值給數(shù)組凉馆,而是定義了一個(gè)匿名函數(shù)薪寓,并將立即執(zhí)行該匿名函數(shù)的結(jié)果賦給數(shù)組亡资。這里匿名函數(shù)有一個(gè)參數(shù),也就是最終的函數(shù)要返回的值向叉。在調(diào)用每個(gè)匿名函數(shù)時(shí)锥腻,我們傳入了變量i。由于函數(shù)參數(shù)是按值傳遞的母谎。所以num的值就是每次調(diào)用時(shí)變量i的值瘦黑。而在這個(gè)匿名函數(shù)內(nèi)部,又創(chuàng)建并返回了一個(gè)訪問num的閉包奇唤。這樣一來幸斥,arr數(shù)組中每個(gè)函數(shù)都有自己num變量的一個(gè)副本。因此就可以返回各自不同的數(shù)值咬扇。
閉包的作用:
function outer() {
var i = 100;
function inner() {
console.log(i++);
}
return inner;
}
var rs = outer();
rs(); //100
rs(); //101
rs(); //102
解釋:outer 是inner的父函數(shù)甲葬,inner被賦給了一個(gè)全局變量,導(dǎo)致inner會一直在內(nèi)存中懈贺,而inner的存在依賴于outer,因此outer也始終在內(nèi)存中演顾,不會在調(diào)用結(jié)束后被垃圾回收機(jī)制回收。
作用:
- 是可以讀取其他函數(shù)內(nèi)部的變量
- 另一個(gè)是讓這些變量的值始終保持在內(nèi)存中隅居。
接下來讓我們看看閉包的一些形式:
閉包一
function f(){
var b = "b";
return function(){
return b;
}
}
console.log(b);
//b is not defined
接下來,我們來看下f的返回值葛虐。這是另外一個(gè)函數(shù)胎源,該函數(shù)有自己的私有空間,同時(shí)也可以訪問f()的空間和全局空間屿脐,所以b對它來說是可見的涕蚤。因?yàn)閒()是可以在全局空間被調(diào)用的(它是一個(gè)全局函數(shù)),所以我們可以將它的返回值賦值給另一個(gè)全局變量的诵,從而生成一個(gè)可以訪問f()私有空間的新全局函數(shù)万栅。
var n = f();
n();
//b
閉包二
下面這個(gè)例子的最終結(jié)果與之前的結(jié)果相同,但是在實(shí)現(xiàn)方法上存在一些細(xì)微的不同西疤。在這里f()不再返回函數(shù)了烦粒,而是直接在函數(shù)體內(nèi)創(chuàng)建一個(gè)新的全局函數(shù)。
var n;
function f(){
var b = "b";
n = function(){
return b;
}
}
f();
n();//b
我們在f() 中定義了一個(gè)新的函數(shù)代赁,并且沒有在這里使用var語句扰她,因此它應(yīng)該是屬于全局的。由于n()是在f()內(nèi)部定義的芭碍,它可以訪問f()的作用域徒役,所以即使該函數(shù)后來升級成了全局函數(shù),但它依然可以保留對f()作用域的訪問權(quán)窖壕。
閉包三
如果一個(gè)函數(shù)需要在其父級函數(shù)返回之后留住對父級作用域的鏈接的話忧勿,就必須要為此建立一個(gè)閉包杉女。
由于函數(shù)通常都會將自身的參數(shù)視為局部變量,因此我們創(chuàng)建返回函數(shù)時(shí)候鸳吸,也可以令其返回父級函數(shù)的參數(shù)熏挎。例如:
function f(arg){
var n = function(){
return arg;
};
arg++;
return n;
}
var m = f(123);
m();
循環(huán)中的閉包
例如:
function f(){
var a = [];
var i;
for(i = 0; i < 3; i++)
{
a[i] = function(){
return i;
}
}
return a;
}
var a = f();
a[0] // 3
a[1] // 3
a[2] // 3
原來我們在這里創(chuàng)建三個(gè)閉包,它們都指向了一個(gè)共同的局部變量i层释。但是婆瓜,閉包并不會記錄它們的值,它們所擁有的只是一個(gè)i的鏈接(也是引用), 因此只能返回i的當(dāng)前值贡羔。由于循環(huán)結(jié)束時(shí)i的值為3廉白,所以這三個(gè)函數(shù)都指向了這一共同的值。
解決方案一
function f(){
var a = [];
var i;
for(i = 0; i< 3; i++)
{
a[i] = (function(x){
return function(){
return x;
}
})(i);
}
}
解決方案二
function f(){
function makeClosure(x){
return function(){
return x;
}
}
var a = [];
var i;
for(i = 0; i < 3; i++){
a[i] = makeClosure(i);
}
return a;
}