深究JavaScript程序员

探索 JavaScript 作用域

2020-05-02  本文已影响0人  deniro

JavaScript 是实际上是一门编译语言。但 JavaScript 并没有多少时间可供编译,在大部分情况下,编译会发生在 JavaScript 代码执行前的几微秒时间内。所以 JavaScript 引擎用了各种办法(比如 JIT)来保证性能。

JIT是“Just In Time”的首字母缩写。每当一个程序在运行时创建并运行一些新的可执行代码,而这些代码在存储于磁盘上时并不属于该程序的一部分,这些可执行代码就是一个JIT。

1 LHS 与 RHS

要说清 JavaScript 的作用域,我们先从赋值操作说起。先来看一段赋值操作代码:

var a = b;

这段变量赋值操作,实际会执行两个动作:

  1. 编译器会判定该变量之前是否还未声明,如果之前没有声明过,则在当前作用域中声明变量;
  2. 然后在运行时,引擎会在作用域中查找该变量,如果能够找到就会对它赋值。

引擎查找变量,有两种类型,一种叫 LHS(Left Hand Side),另一种叫 RHS(Right Hand Side)。LHS 会试图找到变量本身(比如示例代码中的 a), 从而可以对其赋值;而 RHS 会查找某个变量的值,用于真正赋值操作(比如示例代码中的 b)。

RHS 类型查找有多种变体,比如以下这段代码:

function print(x) {
    console.log(x);
}
print(1);

运行结果:

1

2 作用域链

作用域是根据名称查找变量的一套规则。 而在实际情况中, 经常会发生作用域嵌套现象,比如一个块或函数嵌套在另一个块或函数中。在当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找该变量,直到找到或抵达最外层的全局作用域为止。这些一层又一层的作用域,就构成了作用域链。

我们可以把作用域链想象成一栋大楼,第一层表示当前执行作用域,第二层表示嵌套在外层的作用域,以此类推。大楼的顶层表示全局作用域 。LHS 和 RHS 都会先在当前楼层进行查找,如果没有找到,会前往上一层,如果还是没有找到就继续向上,以此类推。此过程会一直持续到顶层,即全局作用域。

3 作用域异常

如果在任何作用域中都无法找到所需要的变量,LHS 和 RHS 对该场景的处理行为并不相同。RHS 查询会抛出 ReferenceError 异常。而 LHS 在非严格模式(ES5 中引入严格模式)下,会自动在全局作用域中创建一个具有该名称的变量。严格模式下,也会抛出 ReferenceError 异常。

如果 RHS 查询找到了一个变量,但是对其进行不合理的操作,比如试图对一个非函数类型的值进行函数调用,或着引用 null 或 undefined 类型的值中的属性等等,这时引擎会抛出 TypeError。 所以,ReferenceError 异常表示作用域中没有找到所需要的变量,而 TypeError 表示已找到,但操作非法。


总结如下:

上一篇 下一篇

猜你喜欢

热点阅读