JavaScript筑基(二):作用域

620 阅读4分钟

作用域

词法作用域变量作用域是JavaScript这门语言中模块化层次的全部体现

词法作用域是基于静态词法分析的

所谓的变量作用域既是静态的,也是动态与静态之间的一座桥梁

从实现的方式来看,一些书籍中称纯粹的词法作用域实现为“静态作用域”,而与代码执行期效果相关的变量作用域则被称为“动态作用域”

词法作用域

一般来说,编程语言中的函数都要解决其变量或成员的可见性问题。而这个可见性的区间,被称为作用域 scope,而当这个域是由静态词法分析而得出的时候,它就被称为词法作用域

具体点看,作用域 scope 是指程序源代码中定义变量的区域。规定了如何查找变量,也就是确定当前执行代码对变量的访问权限

编程语言中的作用域可以简单分为两种:

  • 静态作用域 static scope(词法作用域 lexical scope
  • 动态作用域 dynamic scope

二者的区别在于定义函数的作用域的时机。静态作用域在函数声明时确定,动态作用域在函数执行时确定

很巧的是,JavaScript用的是静态作用域,即函数的作用域在函数声明的时候就已经被确定了,与函数的调用位置无关。

同时,动态作用域在JavaScript中也有对应的表现,即this机制。

函数作用域与块级作用域

JavaScript使用静态作用域,在JavaScript中通常可以继续细分为函数作用域块级作用域,但二者不是对立关系

  • 函数作用域:由函数声明定义,在函数内声明的所有变量在函数体内始终是可见的,可以在整个函数的范围内使用及复用。每有一个函数就会有一个对应的作用域。全局与模块也可以理解为一个函数。
  • 块级作用域:由花括号 {} 标识的形式分块,存在于在函数中。

在一些类C语言中,花括号内的每一段代码都具有各自的作用域,而且用的都是块级作用域,而且变量在声明它们的代码段之外是不可兼得。而在早期的JavaScript中,没有块级作用域这个概念,取而代之地使用了函数作用域。

但实践证明,块级作用域真香,var 是真的垃圾,然后在es6补上了块级作用域的实现。

es6新增了 let/const 来声明仅块级作用域可见的变量

这里可以简单理解为 var 声明的变量只能存在函数作用域中;而 let/const 可以在块级作用域中定义变量,即相关变量只能在对应的块级作用域中使用,不会 “逸出” 到块级作用域所在的函数作用域中。

 if (true) {
   var varA = "varA";
   let letA = "letA";
   for (let i = 0; i < 1; i++) {
     var varB = "varB";
     let letB = "letB";
   }
 }
 console.log(varA, varB);    // varA varB
 console.log(letA, letB);    // ReferenceError: letA is not defined

具体实现细节则需要通过词法环境来解释,下一节《环境记录》会对其进行尽可能详尽的解释

作用域链

在ES3的规范中,存在作用域链 scope chain 的概念,这个内部属性包含了函数被创建的作用域中对象的集合,这个集合被称为函数的作用域链。

scopechain

在ES5中这个概念被词法环境 Lexical Environment 取代

在《红宝书第四版》《权威指南第七版》中对于作用域中变量的引用的解释使用的仍然是作用域链、VO / AO 这一套,在《忍者秘籍第二版》《语言精髓第三版》里面使用的则是词法环境。

变量作用域

词法作用域的提出主要是ECMAScript5的主要成就。ECMA趁机将诸多与变量相关的JavaScript历史特性,统统归纳到“变量作用域”这一概念集合下,形成了独特的“两种作用域”规范体系。

历史中,变量作用域又叫变量的可见性

早期JavaScript的变量作用域,只有所谓的全局变量局部变量两种,并且有且仅有函数全局环境支持变量作用域。而从ES6开始,模块也是支持变量作用域的。

也因为变量作用域是个历史产物的概念集合,所以中是不存在独立的变量作用域的。

在严格模式下,函数和模块的变量作用域是作为词法作用域的一部分来实现的。全局的变量作用域是映射到全局对象的属性上的,并非一个独立的作用域/环境

总的来说,JavaScript中的作用域可以简单的理解为

  • 词法作用域:全局函数模块都与之相关联。
  • 变量作用域:则是全局函数模块才会有的。且在严格模式下,变量作用域是词法作用域的一部分。
  • 函数作用域:通常指同时拥有词法作用域和变量作用域中的一个整体

而这些都只是抽象的概念,下一节会通过作用域的具体实现形式来分析。

参考资料

《JavaScript语言精髓与编程实践》(第三版)

《JavaScript高级程序设计》(第四版)

《JavaScript权威指南》(第七版)

《JavaScript忍者秘籍》(第二版)

Day14 - 词法作用域、块级作用域、作用域链、静态动态作用域

JavaScript深入系列-冴羽

最后

作者水平有限,如果有错误或者不严谨的地方,请务必指出,十分感谢!!!