介绍
本章主要介绍了JavaScript的变量,作用域以及内存问题。
变量
ECMAScript中定义了两种变量,基本类型和引用类型。基本类型存在栈(Stack)中,引用类型存在(Heap)中。
动态的属性
引用类型可以有属性,基本类型不可以。
复制变量值
- 基本类型是值的复制,将一个变量的值赋给另一个变量时
num1 = num2;
,实际上是将num2中的值复制一份出来,再赋给num1。 - 引用类型也是值的复制。先从,实际上存储对象的变量中的“值”是什么开始解释。虽然对象本身在内存中存在堆里,但是指向对象的变量却不是。变量存在栈里,而存在栈里的值,即是在堆中存储的对象的地址。之后就比较好理解,所谓的“值传递”,在对象这个情况中,就指的是将对象的地址复制一份,传给另一个变量。
obj1 = obj2;
做的事情是,让obj1和obj2同时指向了同一个对象。那么当对象的属性或者方法改变的时候,将同时反映到obj1和obj2中。传递参数
ECMAScript中的函数传递参数只有值传递,没有地址传递。这一部分感觉在碰到传递对象为参数的时候很难理解。先举两个例子。
示例一:1
2
3
4
5
6function giveName(obj) {
obj.name="Yutong";
}
var person = new Object();
giveName(person);
alert(person.name); // Yutong
示例二:1
2
3
4
5
6
7
8function giveName(obj) {
obj.name="Yutong";
obj = new Object();
obj.name = "Huang";
}
var person = new Object();
giveName(person);
alert(person.name); // Yutong
可以看出示例一中,person对象的属性被修改了,很容易被理解为是引用传递。但是其实是值传递。因为在函数内部的obj得到了一份person这个变量所指向的那个对象的地址。此时obj和person指向的是同一个变量。因为修改obj属性时,就修改了person的属性。
但是实例二中为什么就不成立了呢?
代码运行到obj = new Object();
这一步的时候,做的事情不是将obj所指向的对象重新初始化,而是把obj指向了另一个别的对象,这个对象是新初始化的一个Object对象。因此之后对obj的操作,将于person指向的对象没有关联。
Nicholas老实说,Think of function arguments in ECMAScript as nothing more than local variables. 我感觉还是需要再理解。
检测类型
- typeof
typeof可以检测Number,String,Boolean和Undefined,但是Null的值会被判断成Object。 - instanceof
用instanceof检测对象具体是不是一个类型的实例。
执行环境及作用域
- 每个函数都有自己的执行环境。当被执行时,将自身的执行环境push到环境栈中,然后执行完毕时再从栈中pop出来。
- 当代码再一个执行环境中执行的时候,会为变量对象创建一个作用域链,链的顶端是当前的执行环境中的变量对象。当代码中需要一个变量时,会从作用域链的顶端往下回溯,一直到找到了为止。
- 延长作用域链
通常只有全局和局部(函数),但是一下特殊的语句也可以将特定的对象添加到作用域链。- try - catch语句中的catch块,可以将抛出的错误对象添加到作用域链顶端。
- with语句,可以把特定对象添加到作用域顶端。
垃圾收集
标记清除(mark-and-sweep)
- 当变量进入执行环境时,进行标记1。
- 当变量退出执行环境时,进行标记2。
- 垃圾收集时,先将所有的变量都标记3,再把所有环境中的变量,以及环境中变量引用的变量清除标记3,然后把有标记3的变量清除。
引用计数
- 跟踪变量被引用的次数。当引用次数变成0,即可回收。
- 问题在于如果存在循环引用,
obj1.name = obj2.name; obj2.age = obj1.age;
在这种情况下,obj1和obj2的引用次数永远都是2,即永远不能再被清除。
管理内存
- 解除变量的引用,可以帮助消除循环引用,并且让可回收的对象及时被回收。