【JavaScript】变量、作用域和内存问题

以下内容整理自《JavaScript高级程序设计(第四章)》

1 基本类型和引用类型的值

基本类型值是简单数据段,引用类型值是多个值构成的对象。

基本数据类型:Undefined、Null、Boolean、Number、String。

对于引用类型值,可以对其添加属性和方法,而基本类型值不支持该操作。

基本类型值的复制是在新变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。而复制引用类型值时,同样会将存储在变量对象中的值复制一份到为新变量分配的空间中,不过这个值实际是一个指针,而这个指针指向存储在堆中的一个对象。

ECMAScript中所有函数的参数都是按值传递的,基本类型值的传递如同基本类型变量的复制一样,而引用类型值的传递,则如同引用类型变量的复制一样。访问变量有按值和按引用两种方式,而参数只能按值传递。

typeof操作符是确定一个变量是字符串、数值、布尔值、还是undefined的最佳工具,如果变量的值是一个对象或null,则typeof操作符会返回object。在检测引用类型值时,为了知道值是什么类型的对象,ECMAScript提供了instanceof操作符,其语法如下:

result = variable instanceof constructor

如果变量是给定引用类型的实例,那么instanceof操作符会返回true

2 执行环境及作用域

执行环境(execution context,简称“环境”)定义了变量和函数有权访问的其他数据,每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中。

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

当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象(activation object)作为变量对象。活动对象在最开始只包含一个变量,即argument对象(该对象在全局环境中不存在)。作用域链中的下一个变量对象来自包含(外部)环境,而再下一个变量对象则来自下一个包含环境。这样,一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。

执行环境的类型只有全局和局部(函数),部分语句可以在作用域链的前端临时增加一个变量对象,该变量对象会在代码执行后被移除,当执行流进入下列任何一个语句时,作用域链就会得到加长:

  1. try-catch语句的catch块;
  2. with语句。

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

JavaScript没有块级作用域,使用var声明的变量会自动被添加到最接近的环境中,如果初始化变量时没有使用var声明,该变量会自动被添加到全局环境。

当在某个环境中为了读取或写入而引用一个标识符时,必须通过搜索来确定该标识符实际代表什么,搜索过程从作用域链的前端开始,向上逐级查询与给定名字匹配的标识符。

3 垃圾收集

垃圾收集器必须跟踪哪个变量有用哪个变量没用,对于不再有用的变量打上标记,以备将来收回其占用的内存。用于标识无用变量的策略通常有两种。

JavaScript中最常用的垃圾收集方式是标记清除(mark-and-sweep),垃圾收集器在运行时会给存储在内存中的所有变量都加上标记。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾收集器完成内存清除工作,销毁那些带标记的值并回收他们所占用的内存空间。

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

引用计数策略面临一个问题:循环引用。循环引用指的是对象A中包含一个指向对象B的指针,而对象B中也包含一个指向对象A的引用。因此必须在不使用值的时候将引用手动置为null,这样就切断变量和它引用的值之间的连接。当垃圾收集器下次运行时,就会删除这些值并回收它们占用的内存。

优化内存占用的最佳方式,就是为执行中的代码只保存必要的数据。一旦数据不再有用,最好通过将其值设置为null来释放其引用——这个做法叫做解除引用(dereferencing)。这一做法适用于大多数全局变量和全局对象的属性。局部变量会在它们离开执行环境时自动被解除引用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值