第 4 章 变量、作用域与内存
4.1 原始值与引用值
JavaScript 不允许直接访问内存位置。
原始值大小固定,因此保存在栈内存上。引用值是对象,存储在堆内存上。
包含引用值的变量实际上只包含指向相应对象的一个指针,而不是对象本身。
从一个变量到另一个变量复制引用值只会复制指针,因此结果是两个变量都指向同一个对象。
原始值和引用值的终极面试题
var foo = { bar: 1 }
var arr1 = [1, 2, foo]
var arr2 = arr1.slice(1)
arr2[0]++
arr2[1].bar++
foo.bar++
arr1[2].bar++
console.log(arr1[1] === arr2[0])
console.log(arr1[2] === arr2[1])
console.log(foo.bar)
4.1.1 动态属性
给原始值添加属性不会报错
let name = 'LBJhui'
name.age = 28
console.log(name.age) // undefined
4.1.2 复制值
通过变量把一个原始值赋值到另一个变量时,原始值会被复制到新变量的位置。在把引用值从一个变量赋给另一个变量时,存储在变量中的值也会被复制到新变量所在的位置。区别在于,这里复制的值实际上是一个指针,它指向存储在堆内存中的对象。操作完成后,两个变量实际上指向同一个对象,因此一个对象上面的变化会在另一个对象上反映出来
4.1.3 传递参数
ECMAScript 中所有函数的参数都是按值传递的。
4.1.4 确定类型
typeof 操作符最适合用来判断一个变量是否为原始类型。
result = variable instanceof constructor
4.2 执行上下文与作用域
通过 var 定义的全局变量和函数都会成为 window 对象的属性和方法。使用 let 和 const 的顶级声明不会定义在全局上下文中,但在作用域链解析上效果是一样的。
❑ 执行上下文分全局上下文、函数上下文和块级上下文。
❑ 代码执行流每进入一个新上下文,都会创建一个作用域链,用于搜索变量和函数。
❑ 函数或块的局部上下文不仅可以访问自己作用域内的变量,而且也可以访问任何包含上下文乃至全局上下文中的变量。
❑ 全局上下文只能访问全局上下文中的变量和函数,不能直接访问局部上下文中的任何数据。
❑ 变量的执行上下文用于确定什么时候释放内存。
4.2.1 作用域链增强
❑ try/catch 语句的 catch 块
❑ with 语句
会导致在作用域链前端临时添加一个上下文,这个上下文在代码执行后会被删除。
4.2.2 变量声明
1.使用 var 的函数作用域声明
变量提升
2.使用 let 的块级作用域声明
作用域是块级的。块级作用域由最近的一对包含花括号{}
界定。
let
与 var
的另一个不同之处是在同一作用域内不能声明两次。重复的 var
声明会被忽略,而重复的 let
声明会抛出 SyntaxError。
暂时性死区 (temporal dead zone)
3.使用 const 的常量声明
使用 const
声明的变量必须同时初始化为某个值。一经声明,在其生命周期的任何时候都不能再重新赋予新值。
const
声明只应用到顶级原语或者对象。换句话说,赋值为对象的 const
变量不能再被重新赋值为其他引用值,但对象的键则不受限制。
如果想让整个对象都不能修改,可以使用 Object.freeze()
,这样再给属性赋值时虽然不会报错,但会静默失败:
const o3 = Object.freeze({})
o3.name = 'Jake'
console.log(o3.name) // undefined
4.标识符查找
4.3 垃圾回收
❑ 离开作用域的值会被自动标记为可回收,然后在垃圾回收期间被删除。
❑ 主流的垃圾回收算法是标记清理,即先给当前不使用的值加上标记,再回来回收它们的内存。
❑ 引用计数是另一种垃圾回收策略,需要记录值被引用了多少次。JavaScript 引擎不再使用这种算法,但某些旧版本的 IE 仍然会受这种算法的影响,原因是 JavaScript 会访问非原生 JavaScript 对象(如 DOM 元素)。
❑ 引用计数在代码中存在循环引用时会出现问题。
❑ 解除变量的引用不仅可以消除循环引用,而且对垃圾回收也有帮助。为促进内存回收,全局对象、全局对象的属性和循环引用都应该在不需要时解除引用。
4.3.1 标记清理
4.3.2 引用计数
循环引用时无法回收内存
4.3.3 性能
在 IE 中,window.CollectGarbage()
方法会立即触发垃圾回收。在 Opera 7 及更高版本中,调用 window. opera.collect()
也会启动垃圾回收程序。
4.3.4 内存管理
及时解除引用