JavaScript作用域链

编程语言中的作用域可分为:

  • 词法作用域:大多数

  • 动态作用域

JS 中只支持词法作用域, this 机制某种程度上很像动态作用域。

词法域所见即所得,易于理解。

闭包是基于词法域书写代码时产生的自然结果。

词法作用域

书写位置决定作用域。

欺骗词法:在运行时修改作用域。

  • eval:运行时植入JS代码。

  • with:运行时指定作用域。

闭包是基于词法作用域

作用域(scope)

作用域指的是 变量 的作用域。作用是隔离变量,限制一个变量的影响范围。

变量的作用域是在定义时就确定的,对代码进行静态分析就可以确定。

词法作用域:定义在词法阶段的作用域,也叫做静态作用域。

动态作用域:不关心定义,只关心调用,运行时确定,作用域链基于调用栈。

ES5 作用域:

  • 全局作用域
  • 函数作用域:function
  • 块级作用域

全局作用域

  • 在全局声明

  • 未声明直接使用

函数作用域

在函数内部声明的变量

块级作用域

隐形块级作用域

  • with

  • catch 子句

ES6 开始正式支持:

  • let

  • const

作用域链(Scope Chain)

在 JavaScript 中,函数也是对象,它的 [[Scope]] 内部属性包含了函数被创建的作用域中对象的集合,这个集合被称为函数的作用域链。

内部属性仅供 JavaScript 引擎访问

作用域链决定了哪些数据能被函数访问。

一般来说,函数的作用域链至少包括其本身作用域和全局作用域。

函数与函数的嵌套形成了作用域链

作用域链的本质上是一个指向“变量对象”的指针列表。

作用域链的第一个对象始终都是当前执行上下文的变量对象,全局执行上下文的变量对象始终是作用域链的最后一个对象。

变量提升和函数提升

在作用域中 var变量函数 的声明会被提升到作用域的开头。

并且,当函数名和 var 变量名相同时,函数名会覆盖变量名。

函数表达式不会提升。

变量查询机制

  • 左查询(LHS):查询变量的内存地址。如果在作用域链上查询不到,默认情况下就会创建一个全局变量,严格模式下则会抛出 ReferenceError 异常。

  • 右查询(RHS):查询变量的值。在作用域链上查询不到则会抛出 ReferenceError 异常。