三點註意事項 "JS作用域傳送門" JS沒有塊級作用域,只有全局作用域和局部作用域(函數作用域)。 JS中的作用域鏈,內部的作用域可以訪問到外部作用域中的變數和方法,而外部作用域不能訪問內部作用域的變數和方法。 當前作用域沒有此變數或方法,會向外部作用域尋找變數或方法。 閉包的兩種使用場景 函數作為 ...
三點註意事項
- JS沒有塊級作用域,只有全局作用域和局部作用域(函數作用域)。
- JS中的作用域鏈,內部的作用域可以訪問到外部作用域中的變數和方法,而外部作用域不能訪問內部作用域的變數和方法。
- 當前作用域沒有此變數或方法,會向外部作用域尋找變數或方法。
閉包的兩種使用場景
函數作為返回值
function f() {
var a = 100;
return function () {
console.log(a);
}
}
var fn = f();
var a = 200;
fn(); // 輸出100
調用fn函數,輸出a
的值,fn中並沒有定義a
,所以會向上找a
,在f函數的作用域中,有a
,值為100。所以就會輸出100,並不會輸出200。全局作用域中的a
和f函數作用域中的a
並不相同。這也體現出了閉包的一個好處:不會造成全局變數污染。
函數作為參數
function f() {
var a = 100;
return function () {
console.log(a);
}
}
var fn = f();
function f2(fn) {
var a = 200;
fn();
}
f2(fn);// 輸出100
調用f2函數,傳入fn函數,調用fn函數,輸出a為100。
關於for迴圈和閉包之間的關係
var arr = [];
var i;
for (i = 0; i < 3; i++) {
arr[i] = function () {
return i;
}
}
console.log(arr[0]()); // 輸出2
arr[0]()
的結果,按照一般的思路來講,應該是0才對,為什麼是2呢?
- for迴圈3次,i的值從0變為2,arr這個數組中也添加了3個函數。
- 當調用
arr[0]
函數時,for迴圈已經結束了,這時候i的值已經為2了,所以arr[0]
函數取到的值為2。
如何解決這個問題?
var arr = [];
var i;
for (i = 0; i < 3; i++) {
arr[i] = (function (i) {
return function () {
return i;
}
})(i);
}
console.log(arr[0]()); // 輸出0
這裡,使用自執行匿名函數構造成一個獨立作用域。每一次for迴圈的時候,都會執行這個匿名函數,並生成 一個匿名函數作用域。
比如第一次迴圈的時候,i = 0
,將i作為參數傳入匿名函數中,這樣i的值就被保存在匿名函數作用域中。當調用arr[0]
函數時,arr[0]
就會取到匿名函數中的i的值。
閉包的實際應用
// 閉包實際應用中主要是封裝變數,收斂許可權
function isFirstLoad() {
var _list = [];
return function (id) {
if (_list.indexOf(id) > 0) {
return false;
} else {
_list.push(id);
return true;
}
}
}
var firstLoad = isFirstLoad();
firstLoad(1); // 返回true
firstLoad(1); // 返回false
firstLoad(2); // 返回true
閉包的缺點
目前主流瀏覽器採用的垃圾收集策略均為標記清除。當變數進入環境(比如,定義一個變數)時,就將這個變數標記為‘進入環境’,當變數離開環境時,就會被標記為‘離開環境’,就會被銷毀。在剛纔的閉包實際應用中_list
變數一直被isFirstLoad
的返回函數引用著,不會隨著isFirstLoad
的調用結束而銷毀。所以_list
變數會一直存在記憶體中。因此不能濫用閉包,否則就會造成網頁性能問題,甚至記憶體泄漏。當我們不需要這些變數的時候,我們可以把這些變數的值賦值為null
。