《JavaScript高级程序设计》第四章笔记

介绍

本章主要介绍了JavaScript的变量,作用域以及内存问题。

变量

ECMAScript中定义了两种变量,基本类型和引用类型。基本类型存在栈(Stack)中,引用类型存在(Heap)中。

动态的属性

引用类型可以有属性,基本类型不可以。

复制变量值

  • 基本类型是值的复制,将一个变量的值赋给另一个变量时num1 = num2;,实际上是将num2中的值复制一份出来,再赋给num1。
  • 引用类型也是值的复制。先从,实际上存储对象的变量中的“值”是什么开始解释。虽然对象本身在内存中存在堆里,但是指向对象的变量却不是。变量存在栈里,而存在栈里的值,即是在堆中存储的对象的地址。之后就比较好理解,所谓的“值传递”,在对象这个情况中,就指的是将对象的地址复制一份,传给另一个变量。obj1 = obj2;做的事情是,让obj1和obj2同时指向了同一个对象。那么当对象的属性或者方法改变的时候,将同时反映到obj1和obj2中。

    传递参数

    ECMAScript中的函数传递参数只有值传递,没有地址传递。这一部分感觉在碰到传递对象为参数的时候很难理解。先举两个例子。
    示例一:
    1
    2
    3
    4
    5
    6
    function giveName(obj) {
    obj.name="Yutong";
    }
    var person = new Object();
    giveName(person);
    alert(person.name); // Yutong

示例二:

1
2
3
4
5
6
7
8
function 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,即永远不能再被清除。

管理内存

  • 解除变量的引用,可以帮助消除循环引用,并且让可回收的对象及时被回收。