第4章 变量、作用域和内存问题

38 阅读5分钟

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

ECMAScript 变量可能包含两种不同数据类型的值:基本类型值和引用类型值。基本类型值指的是 简单的数据段,而引用类型值指那些可能由多个值构成的对象。

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

在对一个变量赋值为对象时,该变量保存的不是实际的对象。为此,引用类型的值是按引用访问的。

4.1.1 动态的属性

对于引用类型的值,我们可以为其添加属性和方法,也可以改变和删除其属性和方法。

4.1.2 复制变量值

复制基本类型值

从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。

复制引用类型值

将存储在变量对象中的值复制一份放到为新变量分配的空间中。而复制的变量是引用类型存储的是指针(指向存储在堆中的一个对象)。此时两个变量实际上将引用同一个对象。

企业微信截图_16849195119882.png

4.1.3 传递参数

ECMAScript 中所有函数的参数都是按值传递的。如果值是基本类型,则复制的是该基本类型值。如果是引用类型,则复制的是该引用类型的地址指针。

4.1.4 检测类型

typeof 操作符

var s = "Nicholas";
var b = true;
var i = 22;
var u;
var n = null;
var o = new Object();
funciton fn(){}
alert(typeof s); //string
alert(typeof i); //number
alert(typeof b); //boolean
alert(typeof u); //undefined
alert(typeof n); //object
alert(typeof o); //object
alert(typeof fn); //function

instanceof 操作符

当我们想知道想知道它是什么类型的对象时,我们就使用instanceof操作符。语法如下:

result = variable instanceof constructor

根据规定,所有引用类型的值都是 Object 的实例。因此,在检测一个引用类型值和 Object 构造函数时,instanceof 操作符始终会返回 true。当然,如果使用 instanceof 操作符检测基本类型的值,则该操作符始终会返回 false,因为基本类型不是对象。

4.2 执行环境及作用域

执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中。

全局执行环境被认为是 window 对象,因此所有全局变量和函数都是作为 window 对象的属性和方法创建的。所以,对象中的方法和函数并没有什么区别。

每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。

作用域链:当代码在一个环境中执行时,会创建变量对象的一个作用域链。它的用途是保证对执行环境有权访问的所有变量和函数的有序访问。

作用域链的前端,始终都是当前执行的代码所在环境的变量对象。末端始终是全局执行环境的变量对象。

标识符解析是沿着作用域链一级一级地从前端到末端搜索标识符的过程。一旦找到标识符就停止搜索,最终找不到会报错。

4.2.1 延长作用域链

有些语句可以在作用域链的前端临时增加一个变量对象,该变量对象会在代码执行后被移除。

try-catch 语句的 catch 块

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

with语句(不要用)

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

4.2.2 没有块级作用域

ES6之前是没有块级作用域的。为了兼容旧版本,在一个代码块{}中,ES6中let,const 声明的有块级作用域。用var声明的依然没有块级作用域。

4.3 垃圾收集

JavaScript 具有自动垃圾收集机制,也就是说,执行环境会负责管理代码执行过程中使用的内存。

4.3.1 标记清除

JavaScript 中最常用的垃圾收集方式是标记清除(mark-and-sweep)。

当变量进入环境(例如,在函数中声明一个变量)时,就将这个变量标记为“进入环境”。而当变量离开环境时,则将标记为“离开环境”。最后,垃圾收集器完成内存清除工作,销毁那些带标记“离开环境”的值并回收它们所占用的内存空间。

4.3.2 引用计数

引用计数的含义是跟踪记录每个值被引用的次数。

当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是 1。如果同一个值又被赋给另一个变量,则该值的引用次数加 1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减 1。这个值的引用次数变成 0 时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾收集器下次再运行时,它就会释放那些引用次数为零的值所占用的内存。