【读书笔记】JS高级程序设计4章--作用域+垃圾收集

一、执行环境及作用域

概念:

执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。
每个执行环境都有一个 与之关联的变量对象 ,环境中定义的所有变量和函数都保存在这个对象中。
 
每个函数都有自己的 执行环境 。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。 而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。
当代码在一个环境中执行时,会创建变量对象的一个 作用域链 。作用域链的用途,是有序访问执行环境所能够访问的所有变量和函数。作用域链的前端,始终都是当前执行函数所在环境的变量对象,也叫做活动对象。
function compare(value1, value2){ 
  if (value1 < value2){ 
    return -1; 
  } else if (value1 > value2){ 
    return 1; 
  } else { 
    return 0; 
  } 
} 
var result = compare(5, 10);

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

 

 

 

1.1 延长作用域链

执行环境的类型总共只有两种——全局和局部(函数),但还是有其他办法来延长作用域链。
延长作用域链的原理: 有些语句可以在作用域链的前端临时增加一个变量对象,该变量对象会在代码执行后被移除
  • try-catch 语句的 catch 块;
  • with 语句。
这两个语句都会在作用域链的前端添加一个变量对象。
对 with 语句来说,会将指定的对象添加到 作用域链中。
catch 语句来说,会创建一个新的变量对象,其中包含的是被抛出的错误对象的声明。
 

1.2 没有块级作用域

对于有块级作用域的语言来说, for 语句初始化变量的表达式所定义的变量,只会存在于循环的环 境之中。而对于 JavaScript 来说,由 for 语句创建的变量 i 即使在 for 循环执行结束后,也依旧会存在 于循环外部的执行环境中。

 

 

二、垃圾收集

JavaScript 具有自动垃圾收集机制,执行环境会负责管理代码执行过程中使用的内存。
垃圾收集机制的原理:找出不再继续使用的变量,释放其占用的内存。为此,垃圾收集器会按照固定的时间间隔,周期性地执行这一操作。
两个策略:标记清除,引用计数
 
 
 
2.1 标记清除(常用)
 
标记:当变量进入环境(例如,在函数中声明一个变量)时,就将这个变量标记为“进入环境”。而当变量离开环境时,则将其标记为“离开环境”。
 
垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记(可以使用任何标记方式)。然后去掉环境中的变量以及被环境中的变量引用的变量的标记。之后还有标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾收集器完成内存清除 工作,销毁那些带标记的值并回收它们所占用的内存空间

 

2.2 引用计数(不常用)

引用计数:跟踪记录每个值被引用的次数。
 
当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是 1 。如果同一个值又被赋给另一个变量,则该值的引用次数加 1 。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减 1 。当这个值的引用次数变成 0 时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾收集器下次再运行时,它就会释放那些引用次数为零的值所占用的内存。
 
这个方法不常用的原因是:引用循环导致的问题。
引用循环:对象 A 中包含一个指向对象 B 的指针,而对象 B 中也包含一个指向对象 A 的引用。
function problem(){ 
 var objectA = new Object(); 
 var objectB = new Object(); 
 objectA.someOtherObject = objectB; 
 objectB.anotherObject = objectA; 
}
在这个例子中, objectA objectB 通过各自的属性相互引用;也就是说,这两个对象的引用次数都是 2,对象得不到回收
避免循环引用问题,最好是在不使用它们的时候手工断开对象与其引用之间的连接。
objectA.someOtherObject = null; 
objectB.anotherObject = null; 

 

 

总结:

执行环境与作用域

所有变量(包括基本类型和引用类型)都存在于一个执行环境(也称为作用域)当中,这个执行环境决定了变量的生命周期,以及哪一部分代码可以访问其中的变量。以下是关于执行环境的几点总结:
  • 执行环境有全局执行环境(也称为全局环境)和函数执行环境之分;
  • 每次进入一个新执行环境,都会创建一个用于搜索变量和函数的作用域链;
  • 函数的局部环境不仅有权访问函数作用域中的变量,而且有权访问其包含(父)环境,乃至全局环境;
  • 全局环境只能访问在全局环境中定义的变量和函数,而不能直接访问局部环境中的任何数据;
  • 变量的执行环境有助于确定应该何时释放内存。

 

垃圾回收机制

JavaScript 是一门具有自动垃圾收集机制的编程语言,开发人员不必关心内存分配和回收问题。可以对 JavaScript 的垃圾收集例程作如下总结。
  • 离开作用域的值将被自动标记为可以回收,因此将在垃圾收集期间被删除。
  • “标记清除”是目前主流的垃圾收集算法,这种算法的思想是给当前不使用的值加上标记,然后再回收其内存。
  • 另一种垃圾收集算法是“引用计数”,这种算法的思想是跟踪记录所有值被引用的次数。JavaScript引擎目前都不再使用这种算法;但在 IE 中访问非原生 JavaScript 对象(如 DOM 元素)时,这种算法仍然可能会导致问题。
  • 当代码中存在循环引用现象时,“引用计数”算法就会导致问题。
  • 解除变量的引用不仅有助于消除循环引用现象,而且对垃圾收集也有好处。为了确保有效地回收内存,应该及时解除不再使用的全局对象、全局对象属性以及循环引用变量的引用。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值