闭包和高阶函数
- 函数式语言的鼻祖是 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