理解javascript的闭包

阅读前提
  • 了解javascript的自执行匿名函数
  • 了解javascript的对象
  • 了解javascript执行环境和执行环境对象
  • 了解javascript作用域链
开始吹水

理解javascript的闭包,必须知道javacript的垃圾回收机制(垃圾回收器)。像C语言,内存管理是程序猿处理的事情,而对于java和javascript这些语言,则实现了自动释放内存的系统,当代码不再需要的时候,就会被系统自动从内存中回收释放。至于javacript的回收机制是怎么样,自然不再本文的介绍范围之内。


自执行匿名函数

我们先来看下面这个例子,打印结果是undefined,因为函数调用过后,函数内的变量已被系统认为不再使用,因此从内存中移除。

var example = function () {
    var case = 'as a case';
};

example();
// output 'undefined'
console.log(example.case);

如果你知道自执行函数【用’()’将一个匿名函数包起来,后面再加一对’()’表示调用】,那么上面的代码可以用下面的形式改写。同样的,匿名函数内的变量将在函数执行完之后在内存被释放。

(function () {
    var case = 'as a case';
})();

那么现在问题来了,我们该如何在内存中维持变量的存在,使之不被回收呢?
答案是使用闭包

闭包

所以,从内存回收的角度理解闭包,可以用一句话来概括闭包的作用哦。
闭包是阻止垃圾回收起将变量从内存中移除,使得在创建变量的执行环境外面能够访问到该变量。



我们重新改写上面的代码。如果我们将函数内部的变量作为一个对象的属性放入对象中,将这个对象返回,那么我们可以在函数外部通过这个被返回的对象来访问原先的变量。但是,需要注意的是,被访问到的变量是返回对象的属性,该属性的值拷贝自原先函数内部的变量,而函数中的变量会在函数被执行完之后,从内存被移除。也就是说, 我们实际访问到的变量不是对原函数内部变量的引用,而是变量 case在被返回对象的上一个拷贝。

var example = (function () {
    var case = 'as a case';
    return {
        case:case
    }
})();

// out put 'as a case'
console.log(example.case);

让我们重新改写上面的代码。如果我们返回的是一个函数,该函数将返回外层函数的内部变量,那么可以通过这个被返回的函数访问外层函数的内部变量,而且是这次不是拷贝,而是引用!试想一下,嵌套的返回函数所返回的值是外层函数的变量,如果这个变量被从内存释放了,那么返回将是undefined。所以,以这种方式返回,原内部变量会在外层函数执行完之后被保留,因为仍然有函数引用了它,为了保证其他函数正确执行,必须保留它。

var example = (function () {
    var case = 'as a case';
    return {
        exampler:function () {
            return case;
        }
    }
})();

// output 'as a case'
console.log(example.exampler());

我们也可以从作用域链的角度来理解闭包,我们知道,每当创建一个函数对象,就会创建该函数对象的作用域,而且该作用域引用了其上下文(执行环境)的作用域。所以,从作用域的角度,变量case是存在于外层函数的上下文,而外层函数调用了嵌套返回函数,那么被返回函数的上下文就是外层函数,它要继承上下文的作用域链创建自己新的作用域链,因为它会引用外层函数的变量,使之成为其作用域链上的变量。

只要作用域链还存在,那么该变量就能通过某条作用域链被访问到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值