原始网址:http://www.w3schools.com/js/js_function_closures.asp
翻译:
JavaScript 闭包
JS 变量的作用域只有两种:本地作用域、全局作用域。
闭包使 JS 变量“私有化”成为可能。
全局变量
函数能够访问定义在它(函数体)之内的所有变量,例如:
function myFunction() {
var a = 6;
return a * a;
}
然而,函数也可以定义在它(函数体)之外的所有变量,例如:
var a = 6;
function myFunction() {
return a * a;
}
在后面的示例中,a 是全局变量。
在 一个 web 页面中,全局变量属于 window 对象。
全局变量能够被页面(和 window)中的所有脚本使用(和变更)。
在第一个示例中,a 是本地变量。
本地变量只能在其被定义的函数内部使用,该变量对其它函数和脚本“不可见”。
同名的全局变量和本地变量是不同的变量,修改其一,另一变量将不会受到影响。
没有使用 **var** 关键字创建的变量都将成为全局变量,即使这些变量在函数内部被创建。 |
变量的生命周期
全局变量的生命周期与所属应用(window 或 web 页面)的生命周期是一致的。
本地变量的生命周期比较“短暂”,它在函数被调用的过程中,随着变量的创建而开始,随着函数的执行结束而结束。
计数器窘境
假定你想使用变量进行计数相关的操作,并且你希望这个计数器对所有函数而言都是可访问的。
你可能会使用一个全局变量以及一个使计数器增值的函数:
var counter = 0;
function add() {
counter += 1;
}
add();
add();
add();
// counter现在的值是3
计数器(counter)应该只能被 add() 函数改变。
问题是,在不调用 add() 函数的情况下,页面中的任何脚本都可改变计数器(counter)的值。
如果我在函数体内声明计数器(counter),在不调用 add() 函数的情况下,该计数器(counter)的值将是不可改变的:
function add() {
var counter = 0;
counter += 1;
}
add();
add();
add();
// counter现在的值应该是3,但事实却不是
计数器(counter)失效了!每当我调用 add() 函数的时候,计数器(counter)的值都被设置为 1 。
幸运的是,JavaScript 内部函数能够解决目前的问题。
JavaScript 内嵌函数
所有的函数都可访问全局作用域。
事实上,在 JavaScript 环境中,所有的函数均可访问它们“上层”的作用域。
JavaScript 语言支持内嵌函数,内嵌函数可访问其“上层”的作用域。
在以下示例中,内部函数 plus() 访问了父级函数中的 counter 变量:
function add() {
var counter = 0;
function plus() {counter += 1;} // 内嵌函数
plus();
return counter;
}
如果我们能够在外部调用 plus() 函数,那么,计数器窘境将得以解决。
我们还需要找到一种能够使 counter = 0 只执行一次的方式。
我们使用闭包(closure)。
JavaScript 闭包
还记得函数自调用吗?以下示例中的这个函数都做了什么?
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})(); // 函数自调用
add();
add();
add();
// counter现在的值是3
示例解释
变量 add 被指定为一个自调用的函数的返回值。
自调用函数只运行一次,它将计数器(counter)的值初始化为 0,并且返回一个函数表达式。
这种方式使得 add 变量成为一个函数,神奇的部分是它能够访问父级作用域中的计数器(counter)。
这种方式被称为 JavaScript 闭包,它使得函数拥有“私有”变量成为可能。
计数器(counter)受匿名函数作用域的保护,它(counter)只能由 add 函数来变更。
闭包使得函数可以访问其父级作用域,即使父级函数已经执行结束。 |
补充示例(带有缓存功能的函数):斐波那契数列的计算
郑重声明,该示例来源于 http://www.jianshu.com/p/6db489366171 。
// 传统实现
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JavaScript Closures</title>
<script type="text/javascript">
var count = 0;
function fibonacci(n) {
count++;
if(n == 0 || n == 1) return 1;
return fibonacci(n-1) + fibonacci(n-2);
}
console.log(fibonacci(20)); // 10946
console.log(count); // count的值为21891
</script>
</head>
<body>
</body>
</html>
// 改进实现
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JavaScript Closures</title>
<script type="text/javascript">
var data = [ 1, 1 ];
var count = 0;
function fibonacci(n) {
count++;
var v = data[ n ];
if( v === undefined ){
v = fibonacci( n - 1 ) + fibonacci( n - 2 );
data[ n ] = v;
}
return v;
}
console.log(fibonacci(20)); // 10946
console.log(count); // count的值为39
</script>
</head>
<body>
</body>
</html>