匿名函数和闭包

    匿名函数是指函数定义时不指定名称的函数。

    闭包是指有权访问另一个函数作用域的函数。而常见的闭包定义方式就是在一个函数内部再创建一个函数。 如下的红色字体就是一个闭包,当然它也是一个匿名函数:

function A() {
    return function () {
        alert(1);
    }
}

    1.闭包的问题:

    如下的函数将返回一个函数数组,原先定义函数的目的是希望函数数组里面的每个函数执行的时候alert出函数在数组中的索引值,然而每次alert都是10。

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

var funcs = createFunctions();
//全部弹出10
for (var k = 0; k < funcs.length; k++) {
    alert(funcs[k]());
}

     要弄清楚这个问题,需要弄清楚函数的作用域链。

     每个函数调用的时候都存在一个执行环境,而每个执行环境都会对应一个变量对象。例如createFunctions函数在调用时它的执行环境对应的变量对象由this,arguments,i,result对象组成。另外createFunctions函数存在一个内部属性[[scope]],这个属性将指向函数的作用域链,作用域链类似于一个数组A,A[0]=createFunctions的变量对象,A[1]=全局作用域对应的变量对象,全局作用域对应的变量对象由this,arguments组成。其中this指向windows,arguments为null。因为作用域链,当全局作用域和当前作用域存在同名变量的时候,优先访问的是当前作用域的变量,如果该变量在当前作用域不存在,才会沿着作用域链往上查找,直到找到这个变量。基于此,我们分析一下createFunctions中的匿名函数在执行的时候它的作用域链。首先,它的作用域链包含3个变量对象:匿名函数自身的变量对象(this,arguments)、createFunctions的变量对象(this,arguments,i,result)、全局变量对象(this,arguments,createfunctions,funcs)。

    当匿名函数执行时,先从作用域链开始检查自身是否有i这个属性,发现找不到,因此沿着作用域链往上查找,在createFunctions的变量对象中找到i变量,此时这个变量的值是10,这就解释了为什么每次alert的结果都是10。另外值得一提的是匿名函数的this指针指向的是window对象,而不是自己。为了解决这个问题,我们可以采用如下的写法:

function createFunctions() {
    var result = [];
    for (var i = 0; i < 10; i++) {
        result[i] = function (num) {
            return function () {
                return num;
            };
        }(i)
    }
    return result;
}
    改成这样,我们再分析下为什么按照这样的写法就能弹出预期的结果。先看匿名函数的作用域链,包含4个变量对象:自身的变量对象(this,arguments)、function(num)函数的变量对象(this,arguments,num)、createfunctions的变量对象(this,arguments,i,result)、全局变量对象(this,arguments,createfunctions,funcs)。其中function(num)函数的变量对象中的num属性在函数执行的时候已经被赋值,num等于数组下标。这样在匿名函数返回num的时候,沿着作用域链往上查找,从function(num)函数的变量对象里面获取到的num就是正确的。




   


阅读更多
个人分类: Js基础知识
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭