Javascript程序设计
# 4.变量、作用域与内存
# 4.1原始值与引用值
ECMAScript 变量可以包含两种不同类型的数据:原始值和引用值。原始值(primitive value)就是 最简单的数据,引用值(reference value)则是由多个值构成的对象。
在把一个值赋给变量时,JavaScript 引擎必须确定这个值是原始值还是引用值。
原始值:
保存原始值的变量是按值(by value)访问的,因为我们操作的就是存储在变量中的实际值。
保存引用值:
引用值是保存在内存中的对象,JavaScript 不允许直接访问内存位置,因此也就 不能直接操作对象所在的内存空间。在操作对象时,实际上操作的是对该对象的引用(reference)而非 实际的对象本身。为此,保存引用值的变量是按引用(by reference)访问的。
ECMAScript 中所有函数的参数都是按值传递的。这意味着函数外的值会被复制到函数内部的参数 中,就像从一个变量复制到另一个变量一样。
# 4.1.1动态属性
原始值和引用值的定义方式很类似,都是创建一个变量,然后给它赋一个值。不过,在变量保存了 这个值之后,可以对这个值做什么,则大有不同。对于引用值而言,可以随时添加、修改和删除其属性 和方法。
let person = new Object();
person.name = "Nicholas";
console.log(person.name); // "Nicholas"
/* 这里,首先创建了一个对象,并把它保存在变量 person 中。然后,给这个对象添加了一个名为
name 的属性,并给这个属性赋值了一个字符串"Nicholas"。在此之后,就可以访问这个新属性,直到
对象被销毁或属性被显式地删除。*/
2
3
4
5
6
原始值不能有属性,尽管尝试给原始值添加属性不会报错。比如:
let name = "Nicholas";
name.age = 27;
console.log(name.age); // undefined
2
3
4
***注意,***原始类型的初始化可以只使用原始字面量形式。如果使用的是 new 关键字,则 JavaScript 会 创建一个 Object 类型的实例,但其行为类似原始值。下面来看看这两种初始化方式的差异:
let name1 = "Nicholas";
let name2 = new String("Matt");
name1.age = 27;
name2.age = 26;
console.log(name1.age); // undefined
console.log(name2.age); // 26
console.log(typeof name1); // string
console.log(typeof name2); // object
2
3
4
5
6
7
8
# 4.1.2复制值
原始值和引用值在通过变量复制时也有所不同。
在通过变量把一个原始值赋值 到另一个变量时,原始值会被复制到新变量的位置。
引用值从一个变量赋给另一个变量时,存储在变量中的值也会被复制到新变量所在的位置。区 别在于,这里复制的值实际上是一个指针,它指向存储在堆内存中的对象。
# 4.1.3 传递参数
ECMAScript 中所有函数的参数都是按值传递的。这意味着函数外的值会被复制到函数内部的参数 中,就像从一个变量复制到另一个变量一样。如果是原始值,那么就跟原始值变量的复制一样,如果是 引用值,那么就跟引用值变量的复制一样。
当引用值在函数内部被重写后,它就变成了一个指向本地对象的指针。而那个本地对象在函数执行结束时就被销毁了。
function setName(obj) {
obj.name = "Nicholas";
obj = new Object();
obj.name = "Greg";
}
let person = new Object();
setName(person);
console.log(person.name); // "Nicholas"
2
3
4
5
6
7
8
# 4.2 执行上下文与作用域
执行上下文就是JavaScript的执行环境,包括三种全局上下文,函数上下文,eval上下文;
- 上下文中的代码在执行的时候,会创建变量对象的一个作用域链(scope chain)。
- 这个作用域链决定了各级上下文中的代码在访问变量和函数时的顺序。
- 代码正在执行的上下文的变量对象始终位于作用域 链的最前端。
内部上下文可以通过作用域链访问外部上下文中的一切,但外部上下文无法访问内部上下文中的任何东西。上下文之间的连接是线性的、有序的。每个上下文都可以到上一级上下文中去搜索变量和函数,但任何上下文都不能到下一级上下文中去搜索。
# 4.3 垃圾回收
通过自动内存管理实现内存分配和闲置资源回收。基本思路很简单:确定哪个变量不会再使用,然后释放它占用的内存。
JavaScript 最常用的垃圾回收策略是标记清理(mark-and-sweep)。
离开作用域的值会被自动标记为可回收,然后在垃圾回收期间被删除。
主流的垃圾回收算法是标记清理,即先给当前不使用的值加上标记,再回来回收它们的内存。
# 5. 基本引用类型
内部上下文可以通过作用域链访问外部上下文中的一切,但外部上下文无法访问内部上下文中的任何东西。上下文之间的连接是线性的、有序的。每个上下文都可以到上一级上下文中去搜索变量和函数,但任何上下文都不能到下一级上下文中去搜索。