彩世界开奖app官网-彩世界平台官方网址(彩票平台)
做最好的网站
来自 彩世界开奖app官网 2019-12-06 22:04 的文章
当前位置: 彩世界开奖app官网 > 彩世界开奖app官网 > 正文

串讲-解释篇:成效域,效能域链,试行情状,变

由于函数也是一个对象,而且函数对象可以赋值给变量,所以通过变量也能调用该函数。

1. 定义

一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数), 因而这些
变量也是该表达式的一部分.

这样返回的对象也会被回收,或者仅仅回收 counter() 变量对象的话:

函数对象有一个__name__属性,可以拿到函数的名字

2. 简洁理解

  1. 一个函数定义在另一个函数内部
  2. 内部函数在执行时会通过复制它的[[scope]]属性创建作用域链
  3. 所以它会持外部父函数的变量对象的引用, 外部函数在执行完毕后不会马上释放变量对象的内存
  4. 因为作用域链上有外部函数等的变量对象, 所以常用于callback等用途
  5. 内部函数被调用时就会形成"闭包"

1、函数创建时就会创建一个包含全局变量对象(上面案例仅仅是全局)和外部函数变量对象上面案例只有全局,斜体是我加的,后面解释的作用域链(即使后面新增了全局变量,在函数调用时,也是可以访问到在函数调用前声明的全局变量,因为作用域链对于变量对象仅仅是引用);

2、当函数被调用时,会创建一个执行环境(另一种说法:函数的环境会被推入环境栈中),在这个环境里通过复制创建时的作用域链构建当前环境的作用域链;

3、在当前环境里,创建一个变量对象(因为当前环境是函数,所以直接把活动对象作为变量对象)保存函数环境内创建的局部变量;

4、把这个活动对象添加到作用域链的前端(作用域链中现在多了一个活动对象作为变量对象,这个活动对象的优先级高于全局变量对象);

5、函数访问变量时,从前面的作用域链中的变量对象里找,按照从局部到全局的顺序找,如果找到,返回该变量,找到全局也没有找到,报错;

6、如果函数返回一个非函数的变量或者没有把一个嵌套函数存储在某处的属性里,实际上将会返回变量的值(引用类型则为保存在堆内存中的地址,这个引用类型的值在函数执行完毕后不会销毁);

7、函数执行完毕,该环境被销毁(另一种说法:栈将其环境弹出),该函数的作用域链和局部变量对象销毁,但全局变量对象保存;

8、如果函数返回了一个函数N(匿名函数或函数内声明的函数),或者把这个函数N存储在某处的属性里;这时就会有一个外部引用指向NN就不会被当做垃圾回收,并且N所指向的变量对象也不会被当做垃圾回收(因为函数N在创建时,就创建了作用域链,这个作用域链中有对于他的外部函数变量对象和全局变量对象的引用,所以导致了N和N所指向的变量对象都不会被垃圾回收,尽管外部函数的作用域链会被销毁)。(外部函数的执行环境应该也被销毁?

>>> f.__name__
'now'
>>> now.__name__
'now'

3. 深入理解

说到闭包, 就不得不提及变量对象和作用域链.

  1. 每个执行环境(既作用域)都有一个表示变量的对象—变量对象, 全局环境的变量对象始终存在, 局部环境的变量对象(如函数调用等), 则只在函数执行时存在.
  2. 再定义一个函数时, 会预先创建一个包含全局变量对象的作用域链, 保存在函数内部的[[scope]]属性中.
  3. 调用函数时, 会为函数创建一个执行环境, 通过复制函数的[[scope]]属性中的对象构建起执行环境的所需的新作用域链.
  4. 然后会有一个新的活动对象(即调用函数产生的变量对象)被创建并推入执行环境的作用域链的最前端.
  5. 作用域链本质上是一个指向变量对象的指针列表, 就是一个个指针从头到尾链了起来, 每个指针都指向一个变量对象.
  6. 无论什么时候在函数中访问一个变量, 都是从作用域链中搜索相应名字的变量.
  7. 一般来讲, 当函数执行完毕后, 局部活动对象就会被销毁, 内存中仅保存全局作用域, 但闭包不同, 因为闭包情况下, 外部函数的活动对象会被内部函数引用, 当内部函数的活动对象没被销毁时, 外部函数的活动对象是无法被释放的, 所以使用闭包时要注意内存泄露等问题.
  8. 当一个函数内部定义的子函数将会函数(即外部函数)的活动对象添加到他的作用域链中.
  9. 因为, 函数内部定义的匿名函数将会包含外部函数的活动对象, 即作用域链上有外部函数的变量对象, 从而实现了闭包. 因此外部函数不会在执行完后立即销毁活动对象, 因为内部的函数可能在引用. 只要内部函数执行完后, 才会执行销毁.

如果要让 counter() 变量对象被垃圾回收,需要释放引用,我觉得有两种方式:

在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。

然后针对这种情况,高程给出的方案就是

>>> def now():
...     print('2015-3-25')
... 
>>> f=now
>>> f
<function now at 0x7f84f14fda28>
>>> f()
2015-3-25

这个时候的作用域链是这个样子:

 

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

图片 1

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

创建 compare() 函数时,会创建一个预先包含全局变量对象的作用域链,这个作用域链被保存在内部的[[Scope]]属性中。

c.count = null;
c.reset = null;
d.count = null;
d.reset = null;

看例子:

图片 2

函数执行时,代码就是这样:

注意这里把 result 和 compare 函数执行的代码给注释掉了,也就是说,这里仅仅只有一个函数声明语句,在这个时候,就发生了下面的事情;

按上面总结的第1点解释就是:

高程解释:

直到匿名函数被销毁后,createComparisonFunction()的活动对象才会被销毁

JavaScript高级程序设计-第3版

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

2、把这个函数N存储在某处的属性里(书上没有给出案例,此处自己猜测

每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。

另一个说法:

函数执行时,就会创建一个执行环境,某个执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁(全局执行环境直到应用程序退出——例如关闭网页或浏览器——时才会被销毁)。

当调用compare()函数时,会为函数创建一个执行环境,然后通过复制函数的[[Scope]]属性中的对象构建起执行环境的作用域链。此后,又有一个活动对象(在此作为变量对象使用)被创建并被推入执行环境作用域链的前端。对于这个例子中compare()函数的执行环境而言,其作用域链中包含两个变量对象:本地活动对象和全局变量对象。显然,作用域链本质上是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。

1、函数返回了一个函数N

总结:

参考资料:

这篇接:理论篇:作用域,作用域链,执行环境,变量对象,活动对象,闭包

如果函数返回了一个函数N(这里就是匿名函数);这时就会有一个外部引用指向N(赋值给了全局变量compare),N就不会被当做垃圾回收,

并且N所指向的变量对象也不会被当做垃圾回收

因为函数N在创建时,就创建了作用域链,这个作用域链中有对于他的外部函数(这里就是 createComparisonFunction()函数)变量对象和全局变量对象的引用,所以导致了N和N所指向的变量对象都不会被垃圾回收。

此时:

function counter() {
    var n = 0;
    return {
        count: function() {
            return n  ;
        },
        reset: function() {
            n = 0;
        }
    };
}

var c = counter(),
    d = counter();
console.log(c.count()); //0
console.log(d.count()); //0:他们互不干扰;
console.log(c.reset()); //reset() 和 count() 共享状态
console.log(c.count()); //0:因为我们重置了c
console.log(d.count()); //1:而没有重置d

引申:闭包

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

这个匿名函数创建时(被 return 的时候)就会创建一个包含全局变量对象和外部函数(这里就是createComparisonFunction()函数)变量对象的作用域链。

代码:

在匿名函数从createComparisonFunction()中被返回后,它的作用域链被初始化为包含
createComparisonFunction()函数的活动对象和全局变量对象。

高程继续说:

高程解释,就是:

突出的那两行代码是内部函数(一个匿名函数)中的代码,这两行代码访问了外部函数中的变量propertyName。即使这个内部函数被返回了,而且是在其他地方被调用了,但它仍然可 以访问变量propertyName。之所以还能够访问这个变量,是因为内部函数的作用域链中包含
createComparisonFunction()的作用域。

这就是上面总结的最后1点:

这样,匿名函数就可以访问在 createComparisonFunction()中定义的所有变量。更为重要的是,createComparisonFunction() 函数在执行完毕后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象。

换句话说,当createComparisonFunction()函数返回后,其执行环境的作用域链会被销毁,但它的活动对象仍然会留在内存中;直到匿名函数被销毁后,createComparisonFunction()的活动对象才会被销毁,

这个时候作用域链就是这样:

备注,上面总结里,第一点和最后一点,在上面的案例没有完全体现出来,实际上在闭包中体现的更明显一点:

这个地方能理解么?

JavaScript权威指南-第6版-中

这里和上面类似,分析起来也是一样的,因为返回的对象里的两个方法:count 和 reset 的作用域链中都保留着对 counter() 变量对象和全局变量对象的引用,所以 counter() 变量对象不会被垃圾回收,返回的对象也不会被垃圾回收(因为 c,d 还存着对于对象的引用);

c = null;
d = null;

结合上面的代码就是:

代码:

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;
        }
    };
}

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

1、函数创建时就会创建一个包含全局变量对象和外部函数变量对象的作用域链。

8、如果函数返回了一个函数N(匿名函数或函数内声明的函数),或者把这个函数N存储在某处的属性里;这时就会有一个外部引用指向NN就不会被当做垃圾回收,并且N所指向的变量对象也不会被当做垃圾回收(因为函数N在创建时,就创建了作用域链,这个作用域链中有对于他的外部函数变量对象和全局变量对象的引用,所以导致了N和N所指向的变量对象都不会被垃圾回收)。

本文由彩世界开奖app官网发布于彩世界开奖app官网,转载请注明出处:串讲-解释篇:成效域,效能域链,试行情状,变

关键词: JavaScript Web前端 作用域