javascript 闭包浅析

每个函数对象在声明或用表达式定义后都会被赋给一个特殊的内部属性,这个属性无法在代码中访问,但可以在浏览器调试中查看。例如在Chrome的调试界面在WatchExpressions中打印函数对象后,会看到一个<function scope>属性:

这个<functionscope>属性就是函数参考作用域链,是一个链表。头结点"可能"是声明或定义这个函数的函数的活动对象。所谓活动像是指一个函数执行时创建的其内部局部变量,参数等函数体内"一切"的一个集合。链表的下一节"可能"就是再外面那个函数的活动对象。作用域链的底部"总会"有一个Global对象,一般就是window对象。

 

前面有一些名词打上了引号,因为要对此补充说明。虽然函数式一个嵌套一个的,但最里面的函数的作用域链并不一定包含外层函数所有的活动对象。只有这个函数内部出现了对外部某个函数活动对象内变量的访问,才会在它的作用域链里添加这个外部函数的活动对象。而且,这个活动对象也并不包含对应的外部函数内部的所有变量,只包含内部函数访问到的变量。但是,无论是否访问Global对象的变量,Global对象是始终被包含在作用域链底部的。下面这个例子说明这一点:

打印func3函数时看到,作用域链里只包含func1函数的活动对象,且只出现了v1变量。底部仍出现了Global对象。

 

<function scope>属性总是包含该函数的外部函数创建的活动对象,当该函数被调用时,就会创建该函数本身的活动对象。这时,一个[[Scope]]属性就会被创建,内容也是一个链表,只不过该函数本身的活动对象被插在链表的顶部,链表其余部分就是<function scope>的内容。[[Scope]]就是函数调用作用域链,函数体内所有变量的访问都是在这个[[Scope]]内查找的。这也是为什么前面把<function scope>称为参考作用域链的原因。注意,一个函数每次调用时都会创建一个新的活动对象。

 

但在外部函数内部定义的所有内部函数体内,若有引用外部函数变量,则引用到的变量是同一个变量,因为所有内部函数的<function scope>作用域链里都保留外部函数的同一个活动对象的引用,所以引用到这个活动对象里的变量亦是同一个。要认识到,作用域链里的一切都是指针。下面是一个例子:

 

funcGetfuncAdd函数的作用域链中都包含指向funcOuter函数的同一个活动对象的指针。理解这一点后也就理解了下面这个例子,这是个闭包的错误用法:

function User(properties) {
            for (var i in properties) {
                this[ "get" + i ] = function () {
                    return properties[i];
                };
            }
        }
        var user = new User({
            name: "Bob",
            age: 44
        });
        alert(user.getname()); //44
        alert(user.getage()); //44

User这个构造函数将传入的参数里的属性名增加前缀get,并作为构造出的实例的属性,返回参数里对应的值。但两个get函数都返回44,因为这两个get函数的<function scop>里都引用到同一个User的活动对象,而在调用alert时这个活动对象里的变量i的值总是age(for循环后的最终值)。要解决这一问题,可采用如下方法,实际上是在<funciton scope>里增加了一环活动对象,而这一环的活动对象在每次循环创建时都是不同的:

        function User(properties) {
            for (var i in properties) {
                (function() {
                    var j = i;
                    this[ "get" + j ] = function () {
                        return properties[j];
                    };
                }).apply(this);
            }
        }

我们将循环体用一个匿名函数包裹,这样一来,两个get函数的参考作用域链里分别包含两个匿名函数不同的活动对象,这两个活动对象里的变量j一个是name,一个是age。这个解决方法有两点需要留意,一个是var j=i;这一行,一开始我没加这一行,也试过var i=i;,发现都不行,所以若要延长作用域链,得将变量改名。还有一点是apply(this),一开始我没加,以为这个匿名函数的执行环境是User构造函数创建的实例,实际上发现是window对象,

所以要注意自执行匿名函数的执行环境都是window。

 

下面是些闭包的用途:

静态私有变量:

  

单例模式:

new Application()返回的对象随不同,但访问的资源都是components

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值