1

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

  • 基本数据类型是按值访问的,因为可以操作保存在变量中的实际的值

基本类型值在内存中占据固定大小的空间,因此被保存在栈内存中

  • 引用类型的值是保存在内存中的对象。JavaScript不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。为此,引用类型的值是按引用的。

以上关于引用类型的说法不严密,当复制保存着对象的某个变量时,操作的是对象的引用。但在为对象添加属性时,操作的是实际的对象
引用类型的值是对象,保存在堆内存中

1.1 动态的属性

不能给基本类型的值添加属性,尽管这样做不会导致任何错误

1.2 复制变量值

  • 从一个变量向另一个变量复制基本类型的值,会创建这个值的一个副本

  • 包含引用类型值的变量实际上包含的并不是对象本身,而是一个指向该对象的指针

1.3 传递参数

ECMAScript中所有函数的参数都是按值传递的。也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样

1.4 检测类型

确定一个值是哪种基本类型可以使用typeof操作符,而确定一个值是哪种引用类型可以使用instanceof操作符

console.log(typeof s);    //检测变量s是哪种基本类型值
console.log(colors instanceof Array);    //检测变量colors是否引用类型Array

2 执行环境及作用域

每个环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。虽然我们编写的代码无法访问这个对象,但解析器在处理数据时会在后台使用它

2.1 延长作用域链

执行环境的类型总共只有两种——全局和局部(函数),但是还有其他办法来延长作用域链

以下两个语句都会在作用互联的前端添加一个变量对象:

  • try-catch语句的catch块

catch会创建一个新的变量对象,其中包含的是被抛出的错误对象的声明

  • with语句

with语句会将指定的对象添加到作用域链中

2.2 没有块级作用域

if或者for语句中定义的变量在代码块执行结束后,会存在于代码块外部的执行环境中

3 垃圾收集

3.1 标记清除

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

"标记清除"是目前主流的垃圾收集算法

3.2 引用计数

引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个值,则这个值的引用次数减1。当这个值的引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来

这种引用计数策略存在一个严重的问题:循环引用

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

上面例子中,objectA和objectB通过各自的属性互相引用,它们的引用次数永远不会是0。假如这个函数被重复多次调用,就会导致大量内存得不到回收

3.4 管理内存

一旦数据不再有用,最好通过将其值设置为null来释放其引用——这个做法叫做解除引用(dereferencing)

function createPerson(name){
    var localPerson = new Object();
    localPerson.name = name;
    return localPerson;
}
var globalPerson = createPerson("Nicholas");

globalPerson = null;    //手工解除globalPerson的引用

解除一个值得引用并不意味着自动回收该值所占用的内存。解除引用的真正作用是让值脱离执行环境,以便垃圾收集器下次运行时将其回收


Cor
63 声望9 粉丝