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

第七章 函数表达式(一)

作者: 伊凡的一天 | 来源:发表于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;
}

相关文章

  • js - 函数表达式

    第七章 函数表达式 1. 创建函数两种方法 函数表达式特征 1,函数声明: 特征: 函数声明提升,意思执行代码前会...

  • javascript高级程序设计(第7章) -- 函数表达式

    第七章:函数表达式 本章内容: 函数表达式的特征 使用函数实现递归 使用闭包定义私有变量 定义函数的方式有两种,一...

  • 第七章 函数表达式

    第七章 函数表达式 问答 1. 函数声明的语法是? 2. 函数表达式(匿名函数)的语法是? 3. 以下递归语句是否...

  • 2020-01-09:第七章:函数表达式(闭包)

    第七章:函数表达式 函数表达式是js中一个非常强大但又让人困惑的特性,我们前面提到过,函数声明有两种方法:函数声明...

  • 第七章 Caché 函数大全 $CHAR 函数

    第七章 Caché 函数大全 $CHAR 函数 将表达式的整数值转换为相应的ASCII或Unicode字符。 大纲...

  • 《JavaScript高级程序设计》之笔记五

    第七章 函数表达式 1. 定义函数的两种方法 : 2. 递归 : 递归函数是在一个函数通过名字调用自身的情况下构成...

  • 07 | 读JavaScript 高程

    这是第七章函数表达式,这一章涉及函数预编译,闭包,作用域链内容。函数涉及内容繁多。 前情提要 02-1 | 读Ja...

  • 07-1 | 读JavaScript 高程

    这是第七章函数表达式,这一章涉及函数预编译,闭包,作用域链内容。函数涉及内容繁多。今天来看作用域链。 在 06-2...

  • 函数与作用域

    函数声明和函数表达式有什么区别 函数声明function声明一个函数 函数表达式函数表达式不是以function开...

  • 函数与作用域

    函数声明和函数表达式有什么区别 函数声明 函数表达式 区别 函数表达式结束后需要加;表示声明变量结束。 函数表达式...

网友评论

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

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