zoukankan      html  css  js  c++  java
  • 闭包

    有不少开发人员总是搞不清匿名函数和闭包这两个概念。因此经常混用。闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数,如下案例:

    function createComparisonFunction(propertyName) {
        return function (object1,object2) {
            var value1 = object1[propertyName];
            var value2 = object2[propertyName];
            if(value1<value2){
                return -1;
            }else if(value1>value2){
                return 1;
            }else{
                return 0;
            }
        };
    }

    在这个例子中,突出的那两行代码是内部函数(一个匿名函数)中的代码,这两行代码访问了外部函数中的变量propertyName。即使这个内部函数被返回了,而且是在其他地方被调用了,但它仍然可以访问变量propertyName。之所以还能够访问这个变量,是因为内部函数的作用域链中包含createComparisonFunction()的作用域。要彻底搞清楚其中的细节,必须从理解函数第一次被调用的时候都会发生什么入手。

    有关如何创建作用域链以及作用域链有什么作用的细节,对彻底理解闭包至关重要。当某个函数第一次被调用时,会创建一个执行环境,以及相应的作用域链,并把作用域链赋值给一个特殊的内部属性(即[[Scope]]).然后,使用this、arguments和其他命名参数的值来初始化函数的活动对象。但在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处于第三位,........直至作为作用域链终点的全局执行环境。

    在函数执行过程中,为读取和写入变量的值,就需要在作用域链中查找变量。来看下面的例子。

    function compare(value1,value2){
        if(value1<value2){
            return -1;
        }else if(value1>value2){
            return 1;
        }else{
            return 0;
        }
    }
    var result = compare(5,10);

    以上代码先定义了compare()函数,然后又在全局作用域中调用了它,当第一次调用compare()时,会创建一个包含this、arguments、value1和value2的活动对象。全局执行环境的变量对象(包含this、result和compare)在compare()执行环境的作用域链中则处于第二位。图7-1展示了包含上述关系的compare()函数执行时的作用域链

    后台的每个执行环境都有一个表示变量的对象 -----变量对象。全局环境的变量对象始终存在,而像compare()函数这样的局部环境的变量对象,则只在函数执行的过程中存在。在创建compare()函数时,会创建一个预先包含全局变量对象的作用域链,这个作用域链被保存在内部的[[Scope]]属性中。当调用compsre()函数时,会为函数创建一个执行环境,然后通过复制函数的[[Scope]]属性中的对象构建起执行环境的作用域链。此后,又有一个活动对象(在此作为变量对象使用)被创建并被推入执行环境作用域链的前端。对于这个案例中compare()函数的执行环境而言,其作用域链中包含两个变量对象:本地活动对象和全局变量对象。显然,作用域链本质上是一个指向变量对象的指针列表,他只引用但不实际包含变量对象。

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

    在另一个函数内部定义的函数会将包含函数(即外部函数)的活动对象添加到它的作用域链中。因此,在createComparisonFunction()函数内部定义的匿名函数的作用域链中,实际上将会包含外部函数createComparisonFunction()的活动对象。图7-2展示了当下列代码执行时,包含函数与内部匿名函数的作用域链。

    var compare = createComparisonFunction("name");

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

    在匿名函数从createComparisonFunction()中被返回后,他的作用域链被初始化为包含createComparisonFunction()函数的活动对象和全局变量对象。这样,匿名函数就可以访问在createComparisonFunction()中定义的所有变量,更为重要的是,createComparisonFunction()函数在执行完毕后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象。换句话说,当createComparisonFunction()函数返回后,createComparisonFunction()的活动对象才会被销毁,例如:

    //创建函数

    var compareNames = createComparisonFunction('name');

    //调用函数

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

    // 解除对匿名函数的引用(以便释放内存)

    compareNames = null;

    首先,创建的比较函数被保存在变量compareNames中。而通过将compareNames设置为等于null解除该函数的引用,就等于通知垃圾回收例程将其清除。随着匿名函数的作用域链被销毁,其他作用域(除了全局作用域)也都可以安全的销毁了。图7-2展示了调用compareNames()的过程中产生的作用域链之间的关系。

  • 相关阅读:
    poj 3616 Milking Time
    poj 3176 Cow Bowling
    poj 2229 Sumsets
    poj 2385 Apple Catching
    poj 3280 Cheapest Palindrome
    hdu 1530 Maximum Clique
    hdu 1102 Constructing Roads
    codeforces 592B The Monster and the Squirrel
    CDOJ 1221 Ancient Go
    hdu 1151 Air Raid(二分图最小路径覆盖)
  • 原文地址:https://www.cnblogs.com/lvruifang/p/7243435.html
Copyright © 2011-2022 走看看