美文网首页
第七章 函数表达式(一)

第七章 函数表达式(一)

作者: 伊凡的一天 | 来源:发表于2018-05-11 11:21 被阅读12次

    前面提到过,定义函数的方式有两种:函数声明函数表达式。下面分别是使用函数声明和函数表达式定义函数的例子:

    //函数声明
    function functionName(arg0, arg1,arg2){
    }
    
    //函数表达式
    var functionName = function(arg0, arg1, arg2){
    };
    

    函数声明定义的函数具有提升(不在乎定义和调用的前后顺序)的效果,而函数表达式则没有。这是两者唯一的区别。

    1. 闭包

    闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的方式,就是在一个函数内部创建另一个函数。下面是一个例子:

    function createComparisionFunction(propertyName){
        return funtion(obj1, obj2){
            var v1 = obj1[propertyName];
            var v2 = obj2[propertyName];
            if (v1 < v2){
                return -1;
           } else if (v1 > v2){
                return 1;
           }else{
                return 0;
           }
        };
    }
    

    在这个例子中,内部函数中var v1 = obj1[propertyName];的这行语句访问了外部函数中的propertyName变量。即使这个内部函数返回后,被其他函数调用,它依旧能够访问这个propertyName变量。这是因为这个内部函数的作用域链上包含了createComparisionFunction()函数的作用域。

    ECMAScript中每个执行环境(Execution Context)都包含了一个保存变量的对象——变量对象(Variable Object)。全局环境的变量对象始终存在,而局部环境的变量对象,则只有在函数运行时才会被创建。在创建createComparisionFunction()函数时,会先创建一个预先包含全局变量对象的作用域链,这个作用域链保存在函数的内部属性[[Scope]]中。当真正调用函数时,首先会为函数创建一个执行环境,然后通过复制函数[[Scope]]属性中保存的对象构建出一条作用域链。此后,创建一个活动对象(Activation Object,作为变量对象使用)并将此活动对象推入执行环境作用域链的前端。显然,作用域链的本质是一个指向变量对象的指针列表,它只包含一系列指向变量对象的引用。下图清晰的解释了createComparisionFunction()函数的作用域链:


    image.png

    在函数中访问一个变量时,会从该函数的作用域链上搜索具有相应名字的变量。一般来说,当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域(全局执行环境的变量对象)。但是,闭包的情况又有所不同。

    var compare = createComparisonFunction("name"); 
    var result = compare({ name: "Nicholas" }, { name: "Greg" }); 
    

    当匿名函数从createComparisonFunction()函数中返回后,它的作用域链被初始化为包含createComparisonFunction()函数的活动变量和全局变量对象。这样,匿名函数就可以访问createComparisonFunction()函数中定义的所有变量。更为特殊的是,createComparisonFunction()函数执行完毕后,其执行环境的作用域链会被销毁,然而其活动对象却仍然存活,因为匿名函数的作用域链依旧引用着其活动对象。直到匿名函数被销毁后,createComparisonFunction()函数的活动对象才会被回收。

    1.1 闭包与变量

    作用域链的机制引申出一个值得注意的副作用,即闭包只能去得包含函数中变量的最后一个值。因为闭包访问的是整个变量,而不是变量的某个特殊值。下面是一个例子:

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

    上面的例子返回一个函数数组,表面上看,每个函数似乎都会返回自己的索引值。但实际上,每个函数返回的都是10。这是因为每一个匿名函数都保存着createFunctions()函数的活动对象,所以它们引用着同一个变量i。当createFunctions()函数返回后,变量i的值是10,因此每个匿名函数返回的变量i的值都是10。但是,我们可以通过创建另一个匿名函数强行让闭包的行为符合预期:

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

    相关文章

      网友评论

          本文标题:第七章 函数表达式(一)

          本文链接:https://www.haomeiwen.com/subject/puforftx.html