JavaScript 基础(007_闭包)

原始网址: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>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值