作用域、作用域链和词法作用域

作用域

JavaScript 高级程序设计 中对作用域的描述:

所有变量(包括基本类型和引用类型)都存在于一个执行环境(作用域)当中,这个执行环境决定了变量的生命周期,以及哪一部分代码可以访问其中的变量。

JS 中可以分为全局作用域和局部作用域,局部作用域又可以细分为函数级作用域和块级作用域(ES6 中新增加的)。

  • 函数级作用域:函数级作用域中的变量只在函数体及其子函数内是可访问的,一般在函数体中声明的变量在函数级作用域中;
  • 块级作用域:块级作用域中的变量只在其绑定的任意作用域中可访问(通常是 {} 内部),用 let/const 关键字声明的变量在块级作用域中;
  • 全局作用域:全局作用域中的变量可以在代码的任意地方被访问,下面几种情况声明的变量在全局作用域中:
    1. var 关键字, 且没有在函数内声明
    2. 声明的时候没有带 var/let/const 关键字
    3. window 对象的全部属性

作用域也可以理解为是一套规则,用来确定查找变量的范围以及查找变量的方式。
其中方式有 LHS(Left-Hand Side) 和 RHS(Right-Hand Side)

  • LHS:如果查找变量是为了对变量进行赋值(储存数据),比如 a = 1;, 就使用 LHS,之所以用这个缩写是因为此时变量往往在=左边,但是函数在传入实参的时候,实际上是一个隐式的赋值过程,所以也用 LHS 查询。
  • RHS:如果查找变量是为了获取储存在该变量上的数据,比如var b = a;console.log(a);中查找变量 a, 就使用 RHS 查找(变量 b 采用 LHS 查找),之所以用这个缩写是因为此时变量往往在=右边。

作用域链

当一个块或函数嵌套在另一个块或函数内部时,就发生了作用域的嵌套。
因此在当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量,或抵达最外层的作用域为止(即全局作用域)。
这种作用域的嵌套就构成了作用域链。
更深层次的理解看这篇文章JavaScript执行上下文

词法作用域

作用域共有两种主要的工作模型。
第一种是最为普遍的,被大多数编程语言所采用的词法作用域,我们会对这种作用域进行深入讨论。
另外一种叫作动态作用域,仍有一些编程语言在使用( 比如 Bash 脚本、 Perl 中的一些模式等)。

简单地说,词法作用域就是定义在词法阶段的作用域。换句话说,词法作用域是由你在写代码的时候将变量和块作用域写在哪里决定的,因此,当词法分析器处理代码时会保持作用域不变。