閉包和高階函數
- 函數式語言的鼻祖是 LISP
- JavaScript在設計之初參考了 LISP兩大方言之一的 Scheme,引入了 Lambda表達式、閉包、高階函數等特性。
閉包
閉包的形成與變量的作用域以及變量的生存周期密切相關。
變量的作用域
- 變量的作用域,就是指變量的有效范圍。
- 變量的搜索是從內到外而非從外到內的。
變量的生命周期
- 對于全局變量來說,全局變量的生存周期當然是永久的,除非我們主動銷毀這個全局變量。
- 對于局部變量來說,當退出函數時,它們都會隨著函數調用的結束而被銷毀。
var foo = function() {
var a = 1;
return function() {
a++;
alert(a);
}
};
var f = foo();
f(); // 輸出:2
f(); // 輸出:3
f(); // 輸出:4
f(); // 輸出:5
變量
f
返回了一個匿名函數的引用,它可以訪問到foo()
被調用時產生的環境,而局部變量a
一直處在這個環境里。既然局部變量所在的環境還能被外界訪問,這個局部變量就有了不被銷毀的理由。在這里產生了一個閉包結構,局部變量的生命看起來被延續了。
另一個經典例子,我們通過循環來給每個 div 綁定 onclick 事件,按照索引順序,點擊第 1個 div 時彈出0,點擊第 2個 div 時彈出 1,以此類推。
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
var nodes = document.getElementsByTagName( 'div' );
for ( var i = 0, len = nodes.length; i < len; i++ ){
nodes[ i ].onclick = function(){
alert ( i );
}
};
把每次循環的 i 值都封閉起來。當在事件函數中順著作用域鏈中從內到外查找變量 i 時,會先找到被封閉在閉包環境中的 i
閉包的更多作用
封裝變量
閉包可以幫助把一些不需要暴露在全局的變量封裝成“私有變量”。
var mult = function() {
var a = 2;
for (var i = 0, l = arguments.length; i < l; i++) {
a = a * arguments[i];
}
return a;
};
console.log(mult(1, 2, 3)); //輸出 12