7-JavaScript高级程序设计-变量作用域内存

一、基本类型和引用类型的值

基本类型值:简单的数据段;基本数据类型是按值访问的
引用类型值:可能由多个值构成的对象;引用数据类型的值是按引用访问的

1. 动态的属性

只能给引用类型值动态地添加属性;
不能给基本类型的值添加属性(不报错)。

2.复制变量值

从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。复制的值和原来的值完全独立。
从一个变量向另一个变量复制引用类型的值,也会将存储在变量对象中的值(一个指针)复制一份放到为新变量分配的空间中。复制的值和原来的值两个变量将引用指向同一个对象。

3.传递参数

所有函数的参数都是按值传递的。
在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量(即命名参数);
在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部。

function setName(obj) {
  obj.name = 'aaa';
  obj = new Object();
  obj.name = 'bbb';
}
var person = new Object();
setName(person);
alert(person.name);  // 'aaa'

说明参数是按值传递的。当在函数内部重写obj时,这个变量引用的就是一个局部对象,这个局部对象会在函数执行完毕后立即销毁。
函数的参数可以当成局部变量。

4.检测类型
  • typeof
    检测字符串、数值、布尔值、undefined的最佳工具。
    函数返回‘function’,null和对象返回object。
  • instanceof
    语法:variable instanceof constructor
    检测一个引用类型值和Object构造函数时,始终返回true(所有引用类型的值都是Object的实例)
    注意:对正则表达式应用typeof,Safari5及之前和Chrome7及之前返回‘function’,IE和Firefox返回‘object’(ECMA-262规定任何在内部实现call方法的对象应用typeof返回function)

基本类型值和引用类型值 具有以下特点:
 基本类型值在内存中占据固定大小的空间,因此被保存在栈内存中;
 从一个变量向另一个变量复制基本类型的值,会创建这个值的一个副本;
 引用类型的值是对象,保存在堆内存中;
 包含引用类型值的变量实际上包含的并不是对象本身,而是一个指向该对象的指针;
 从一个变量向另一个变量复制引用类型的值,复制的其实是指针,因此两个变量最终都指向同一个对象;
 确定一个值是哪种基本类型可以使用 typeof 操作符,而确定一个值是哪种引用类型可以使用 instanceof 操作符。

二、执行环境及作用域

执行环境(execution context)定义了变量或函数有权访问的其他数据,决定了它们各自的行为。
每个执行环境都有一个与之相关的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中。
web浏览器中,全局执行环境被认为是window对象,全局变量和函数是作为window对象的属性和方法。
某个执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数的定义也随之销毁(全局执行环境直到应用程序退出,如关闭页面或浏览器,才会销毁)。
每个函数都有自己的执行环境
当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。作用域链保证对执行环境有权访问的所有变量和函数的有序访问。
作用域链的前端,始终是当前执行的代码所在环境的变量对象,下一个变量对象来自包含环境,一直延续到全局执行环境,全局执行环境的变量对象始终是作用域链中的最后一个对象。
内部环境可以通过作用域链访问所有的外部环境,外部环境不能访问内部环境中的任何变量和函数。

1. 延长作用域链

执行环境类型只有两种 – 全局和局部(函数)。

延长作用域链,有些语句可以在作用域链的前端临时增加一个变量对象,该变量对象会在代码执行后被移除。

  • try-catch语句的catch块
  • with语句
    这两个语句都会在作用域链的前端添加一个变量对象。对 with 语句来说,会将指定的对象添加到作用域链中。对 catch 语句来说,会创建一个新的变量对象,其中包含的是被抛出的错误对象的声明。
2.没有块级作用域

JavaScript 没有块级作用域。
在其他类 C 的语言中,由花括号封闭的代码块都有自己的作用域(如果用 ECMAScript 的话来讲,就是它们自己的执行环境),因而支持根据条件来定义变量。

for (var i=0; i < 10; i++){
    doSomething(i);
}
alert(i);      //10
  • 声明变量
    使用 var 声明的变量会自动被添加到最接近的环境中。
    在函数内部,最接近的环境就是函数的局部环境;在 with 语句中,最接近的环境是函数环境。如果初始化变量时没有使用 var 声明,该变量会自动被添加到全局环境。
  • 查询标识符
    搜索过程从作用域链的前端开始,向上逐级查询与给定名字匹配的标识符。如果在局部环境中找到了该标识符,搜索过程停止,变量就绪。如果在局部环境中没有找到该变量名,则继续沿作用域链向上搜索。搜索过程将一直追溯到全局环境的变量对象。如果在全局环境中也没有找到这个标识符,则意味着该变量尚未声明。

三、垃圾收集

JavaScript 具有自动垃圾收集机制。(执行环境会负责管理代码执行过程中使用的内存)
原理:找出那些不再继续使用的变量,然后释放其占用的内存。

1. 标记清除

最常用的垃圾收集方式是标记清除 (mark-and-sweep)。
垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记 (当然,可以使用任何标记方式)。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾收集器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。
到 2008 年为止,IE、Firefox、Opera、Chrome 和 Safari 的 JavaScript 实现使用的都是标记清除式的垃圾收集策略(或类似的策略),只不过垃圾收集的时间间隔互有不同。

2.引用计数

另一种不太常见的垃圾收集策略叫做引用计数 (reference counting)。引用计数的含义是跟踪记录每个值被引用的次数。
当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是 1。 如果同一个值又被赋给另一个变量,则该值的引用次数加 1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减 1。当这个值的引用次数变成 0 时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾收集器下次再运行时,它就会释放那些引用次数为零的值所占用的内存。

这种方式存在一个严重的问题:循环引用。

function problem(){
    var objectA = new Object();
    var objectB = new Object();
 objectA.someOtherObject = objectB; 9
    objectB.anotherObject = objectA;
}

问题:即使 IE 的 JavaScript 引擎是使用标记清除策略来实现的,但 JavaScript 访问的 COM 对象依然是基于引用计数策略的。换句话说,只要在 IE 中涉及 COM 对象,就会存在循环引用的问题。
解决方式1:
手动断开,将变量设置为 null 意味着切断变量与它此前引用的值之间的连接。
解决方式2:
IE9 把 BOM 和 DOM 对象都转换成了真正的 JavaScript 对象。避免了两种垃圾收集算法并存导致的问题,也消除了常见的内存泄漏现象。

3.性能问题

垃圾收集器是周期性运行的,而且如果为变量分配的内存数量很可观,那么回收工作量也是相当大的。在这种情况下,确定垃圾收集的时间间隔是一个非常重要的问题。
IE 的性能问题:IE 的垃圾收集器是根据内存分配量运行的,IE7 的 JavaScript 引擎的垃圾收集例程改变了工作方式:触发垃圾收集的变量分配、 字面量和(或)数组元素的临界值被调整为动态修正。
有的浏览器中可以触发垃圾收集过程,但不建议这样做。IE 中调用 window.CollectGarbage()方法会立即执行垃圾收集。Opera 7 及更高版本中,调用 window.opera.collect()也会启动垃圾收集例程。

4.管理内存

JavaScript 在进行内存管理及垃圾收集时,其中最主要的一个问题,就是分配给 Web 浏览器的可用内存数量通常要比分配给桌面应用程序的少。
这样做的目的主要是出于安全方面的考虑,目的是防止运行 JavaScript 的网页耗尽全部系统内存而导致系统崩溃。
内存限制问题不仅会影响给变量分配内存,同时还会影响调用栈以及在一个线程中能够同时执行的语句数量。
确保占用最少的内存可以让页面获得更好的性能。
优化内存占用的最佳方式,就是为执行中的代码只保存必要的数据。一旦数据不再有用,最好通过将其值设置为 null 来释放其引用——这个 做法叫做解除引用 (dereferencing)。
这一做法适用于大多数全局变量和全局对象的属性。局部变量会在它们离开执行环境时自动被解除引用。
解除一个值的引用并不意味着自动回收该值所占用的内存。解除引用的真正作用是让值脱离 执行环境,以便垃圾收集器下次运行时将其回收。


上一篇:6-JavaScript高级程序设计-函数
下一篇:8-JavaScript高级程序设计-Object&Array

全书整理版:《Javascript高级程序设计》第3版(总结版)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值