关于闭包的问题

什么是闭包?
按照通俗的理解就是函数里面的函数,该函数能访问包含它的函数的变量。按照比较官方的理解:闭包是指有权访问另一个函数作用域的变量的函数。For Example:

function funcA() {
    var a = 1, b = 2;
    return funcB();

    function funcB() {
        // 注意,a 和 b 在本函数里根本没有定义,但是竟然能访问到
        return a + b;
    }
}

var a = funcA();
// a 将会等于 3

为什么funcB能访问到已经返回后funcA的变量呢?我们先了解一下函数被调用的过程。
当一个函数被调用后,会创建一个执行环境以及相应的变量对象、作用域链。在之前的一篇文章我曾经介绍过这三个概念。现在简单介绍一下。执行环境中定义了变量或函数有权访问的其他数据,决定了他们各自的行为,也就是说执行环境规定了你能访问什么,不能访问什么。那么这些规则保存在哪里呢?答案是在一个变量对象里面。这个变量保存着环境变量中所有的变量和函数。这个对象是不可以访问的。而作用域链是保证执行环境有权访问的所有变量和函数的有序访问。方向是就近原则。位于作用域前端的变量对象是本身的环境,比如这个环境是函数的话,函数的活动对象就是处于最前端的。以此类推下一个是直接包含环境的变量对象,再下一个是间接的包含环境。。。直到全局执行环境。比如:funcB能访问funcA的变量和函数,也能访问window对象的变量和函数。
还有一个事实是:全局环境的变量在函数执行始终是存在的,这个无可厚非,但是想funcA这个函数只有在函数执行过程中存在,在这之后包括他的执行环境和变量对象都会被销毁,那为什么funcB还能访问funcA的变量对象呢?这其实就是闭包的神秘之处了。
闭包可以将包含它的函数的活动对象(变量对象)添加到它的作用域链中,正是由于闭包的作用域链仍然在引用外部函数的活动对象,所以当funcA返回时,虽然它的执行环境的作用域链被销毁了,但是它的活动对象还在内存中。知道匿名函数会销毁.(因此使用过多的闭包可能占用过多的内存)

闭包带来的副作用
1、闭包只能取得包含函数中任意变量的最后一个值。

function createFunctions(){
    var result =new Array();
    for (var i =0;i<10;i++)
    {
        result[i] = function (){
            return i;
        }
    }
    return result;
}
var result = createFunctions();
alert(result[0]());  //10

表面上看result是返回一个0-9的数组,但resulti结果却是全部是10.原因是当createFunctions()返回时,变量的i值都是10了。
为什么会这样子呢?
我们先理解一下程序的思路,在上面的代码中其实每次执行闭包函数function (){
return i;
的时候,闭包函数都保存了createFunctions函数里面所有的变量,而且每次执行闭包函数的时候createFunctions函数里面的变量都会不变,当执行完createFunctions函数之后,createFunctions里面的i已经变为10了,又由于闭包函数不是立即执行,而是在createFunctions函数已经完全执行完之后执行的,所以最后每次闭包函数访问的都createFunctions完全执行完的变量(此时i已经是10了)。
解决办法:
既然是因为闭包函数不能立即执行的问题,那么我们就利用一个匿名函数通过立即执行把相应的i值立即放进闭包函数里面(此时对于闭包函数而言,i就是一个常数,也就是执行前已经拥有的值)看代码:

function createFunctions(){
    var result =new Array();
    for (var i =0;i<10;i++)
    {
        result[i] = function(num){
            return function(){
                return num;
            }
        }(i);
    }
    return result;
}
var result = createFunctions();
alert(result[0]());  //0

这样的话,闭包里面的num都是匿名函数立即执行时传进来的参数i;
2、this对象的问题
先来看一段代码:

var name ="周运金"var object ={
    name:"李佳家",
    getName:function(){
        return function(){
            this.name;
        }
    }
}
alert(object.getName()());  //"周运金"

最后返回的周运金,也就是说this指针指向了window对象。但是根据之前的理解:当函数在全局变量中执行时,this等于window,当属被作为某个对象的内置方法时this指向该对象。
原因在于匿名函数具有全局性,因此this指针通常指向了window。这种情况下要this指向object也行,采用下面的做法:

var name ="周运金"var object ={
    name:"李佳家",
    getName:function(){
        var that=this;
        return function(){
            that.name;
        }
    }
}
alert(object.getName()());  //李佳家

因为getName这个函数是object纯正的内置函数,所以可以访问this对象,然后将这个对象传给闭包。这也不算是高级的做法,其实有点硬来的意思了,哈哈.

3、引起内存泄漏
这都是IE之前版本的对Jscript的垃圾回收机制惹的祸,这之间文章有提到过垃圾回收机制,常用的是标记清除。很少见的是IE之前版本的引用计数的垃圾机制。由于闭包在外部函数返回后还在使用外部函数的变量对象,所以此时外部函数的变量可能会被循环引用,导致引用次数无法减少,此时就会造成内存泄漏。比如:

function ass(){
var element =document.getElementById('someElement');
    element.onclick =function (){
        alert(element.id);
    };
}

解决方法:

function ass(){
var element =document.getElementById('someElement');
    var id=element.id;
    element.onclick =function (){
        alert(id);
    };
    element=null;
}

改进的代码有:将elment.id的一个副本保存在一个变量中,在闭包中引用该变量,消除了循环引用。最后还得手动清除element.因为即使必要不直接引用element,包含函数的活动对象也会保存一个引用。

以上就是闭包与作用链的问题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值