JavaScript核心-閉包
閉包(Closure)
關於閉包,真的是看了忘忘了看,相信這次應該理解了~
變量的作用域
ES中規定的JavaScript的變量作用域有兩種:全部變量和局部變量(ES中引入塊級作用域)
javascript有兩種特殊的屬性
函數內部可以直接讀取全局變量
var n=999;
function f(){
alert(n);
}
f(); //
函數外部不能夠讀取局部變量
function f(){
var n=999;
}
alert(n); // error
注意 函數內部定義變量使用var,否則你定義的就是一個全局變量
function f(){
n=999;
}
f();
alert(n); //
如何從外部讀取局部變量
上邊講到,正常情況下,外部無法讀取局部變量,想要讀取局部變量怎麼辦?
就是在函數內部在定義一個函數
function f(){
var n=999;
function f(){
alert(n); //
}
}
在上面的代碼中,函數f就被包括在函數f內部,這時f內部的所有局部變量,對f都是可見的。但是反過來就不行,f內部的局部變量,對f就是不可見的。這就是Javascript語言特有的”鏈式作用域”結構(chain scope),子對象會一級一級地向上尋找所有父對象的變量。所以,父對象的所有變量,對子對象都是可見的,反之則不成立.
f可以讀取f中的變量,將f作爲返回值,那麼f的外部我們就可以讀取內部變量了
function f(){
var n=999;
function f(){
alert(n);
}
return f;
}
var result=f();
result(); //
閉包的概念
上述代碼中f就是閉包,下面看看抽象的閉包~
閉包:閉包是一個代碼塊(在ECMAScript是一個函數)和以靜態方式/詞法方式進行存儲的所有父作用域的一個集合體。所以,通過這些存儲的作用域,函數可以很容易的找到自由變量。
看看容易理解的閉包概念
由於在Javascript語言中,只有函數內部的子函數才能讀取局部變量,因此可以把閉包簡單理解成”定義在一個函數內部的函數”。
所以,在本質上,閉包就是將函數內部和函數外部連接起來的一座橋樑。
閉包的用途
可以讀取函數內部的變量
就是讓這些變量的值始終保持在內存中
function f(){
var n=999;
nAdd=function(){n+=}
function f(){
alert(n);
}
return f;
}
var result=f();
result(); //
nAdd();#不要懷疑在外部爲什麼還可以調用,nAdd()是掛載在window下的
result(); //
result其實就是閉包f函數,一共運行兩次,一次二次。這就說明f局部變量一直在內存當中,
並沒有在被調用之後被自動的清楚。
爲什麼會這樣呢?原因就在於f是f的父函數,而f被賦給了一個全局變量,這導致f始終在內存中,而f的存在依賴於f,因此f也始終在內存中,不會在調用結束後,被垃圾回收機制(garbage collection)回收。
注意nAdd的值是一個匿名函數(anonymous function),而這個匿名函數本身也是一個閉包,所以nAdd相當於是一個setter,可以在函數外部對函數內部的局部變量進行操作。
使用閉包的注意點
由於閉包會使得函數中的變量都被保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題,在IE中可能導致內存泄露。解決方法是,在退出函數之前,將不使用的局部變量全部刪除。
閉包會在父函數外部,改變父函數內部變量的值。所以,如果你把父函數當作對象(object)使用,把閉包當作它的公用方法(Public Method),把內部變量當作它的私有屬性(private value),這時一定要小心,不要隨便改變父函數內部變量的值。
思考題
題1
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()());
題2
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()());