閉包(Closure) 本文聚焦於回答2個問題: 1. 在全局作用域中,如何讀取函數內部的局部變數? 2. 在全局作用域中,如何修改函數內部的局部變數? 變數作用域 JavaScript語言的作用域,一句話概括就是:內層函數可以訪問外層函數的變數,而外層函 數不可以訪問內層函數的變數。 在內層函數中 ...
閉包(Closure)
本文聚焦於回答2個問題:
- 在全局作用域中,如何讀取函數內部的局部變數?
- 在全局作用域中,如何修改函數內部的局部變數?
變數作用域
JavaScript語言的作用域,一句話概括就是:內層函數可以訪問外層函數的變數,而外層函
數不可以訪問內層函數的變數。
在內層函數中定義的變數如果沒使用var關鍵詞,則該變數變為全局變數。通過這種方法定義
的全局變數,要在此函數執行後才有效。請看下麵代碼:
function outer() {
n = 820;
function inner() {
he = 835;
}
return inner;
}
// console.log(n);
// n is not defined
// 上面的錯誤會打斷程式執行,如要測試下麵的代碼,需註釋掉上面的代碼
outer();
console.log(n); // 820
// console.log(he);
// n is not defined
(outer())();
console.log(he); // 835
如何從外部讀取局部變數
通過閉包可以實現在全局作用域中訪問函數內部變數。
function outer() {
var n = 820;
function inner() {
return n;
}
return inner;
}
var k = (outer())();
console.log(k); // 820
outer()
函數執行一次,將返回inner()
函數的引用,再執行一次inner()
函數,就成功的把局部變數n
返回出來。從而實現從外部讀取內部變數。
如何從外部修改局部變數
通過閉包可以實現在全局作用域中修改函數內部變數。
var n = "hello";
function outer() {
var n = 820;
function get() {
return n;
}
function inc() {
n++;
}
return {
n: n,
get: get,
inc: inc
};
}
var result = outer();
console.log( result.n ); // 820
console.log( result.get() ); // 820
result.inc();
console.log( result.get() ); // 821
result.inc();
console.log( result.get() ); // 822
console.log( result.n ); // 820
outer()
函數返回一個對象,這個對象有1個屬性,2個方法。正常情況下一個
函數調用完畢,其內部的變數將會被垃圾回收機制(garbage collection)回收。
也就是說這些變數已經不存在記憶體中,也沒有辦法讀取這些變數的值,更沒法修改。
但是,我們把outer()
函數的返回值賦給了一個全局變數,全局變數是始終存在
記憶體中的,而這個全局變數result
又使用到了outer()
函數的局部變數,所以
outer()
函數的局部變數,不會被清除,將一直保存在記憶體中。
因此,只要我們執行一次result.inc()
, outer()
函數裡面的n
就會增加1。
而我們通過result.get()
就可以訪問到outer()
函數裡面的n
。
要註意result.n
的值始終是820,這裡面的820只是n
變數的一個副本,一旦
outer()
函數執行完畢,result.n
就和n
沒有關係了。
註意事項
由於閉包會使得函數中的變數都被保存在記憶體中,記憶體消耗很大,所以不能濫用閉包
,否則會造成網頁的性能問題。
參考資料
http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html