深入JavaScript作用域
作者:Seiya
时间:2019年06月13日
前言
作用域是每种计算机语言最重要的基础之一,当然它也是JavaScript最重要的概念之一。
作用域
作用域是什么
几乎所有编程语言最基本的功能之一,就是能够储存变量当中的值,并且能在之后对这个值进行访问或修改。事实上,正是这种储存和访问变量的值的能力将 状态
带给了程序。但是将变量引入程序带来了几个问题:这些变量在哪里?换句话说,它们储存在哪里?最重要的是,程序需要时如何找到它们?
这些问题说明需要一套设计良好的规则来存储变量,并且之后可以方便地找到这些变量。 这套规则被称为 作用域
。
作用域的简单概念
作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。
理解作用域
要理解作用域,我们从 JavaScript 引擎的角度看看一段程序到底是怎样执行的。
我们有这样一段程序:var a = 2;
遇到
var a
,编译器会询问作用域是否已经有一个该名称的变量存在于同一个作用域的集合中。如果是,编译器会忽略该声明,继续进行编译;否则它会要求作用域在当前作用域的集合中声明一个新的变量,并命名为a
。接下来编译器会为引擎生成运行时所需的代码,这些代码被用来处理
a = 2
这个赋值操作。引擎运行时会首先询问作用域,在当前的作用域集合中是否存在一个叫作a
的变量。 如果存在,引擎就会使用这个变量;如果不存在,引擎会继续向上查找该变量;如果没有找到,引擎就会举手示意并抛出一个异常!
总结:
变量的赋值操作会执行两个动作,首先编译器会在当前作用域中声明一个变量(如果之前没有声明过),然后在运行时引擎会在作用域中查找该变量,如果能够找到就会对它赋值。
注意:LHS查询、RHS查询
- LHS 查询则是试图找到变量的容器本身,从而可以对其赋值;
- RHS 查询理解成retrieve his source value(取到它的源值),及得到某某的值;
作用域嵌套
通俗来讲就是:当一个块或函数嵌套在另一个块或函数中时,就发生了作用域的嵌套。因此,在当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量,或抵达最外层的作用域(也就是全局作用域)为止。
词法作用域
作用域共有两种主要的工作模型。第一种是最为普遍的,被大多数编程语言所采用的 词法作用域
。另外一种叫作 动态作用域
,仍有一些编程语言在使用(比如 Bash 脚本、Perl 中的一些模式等)。
词法作用域和动态作用域
词法作用域
词法作用域是在写代码或者说定义时确定的,关注函数在何处声明 。
词法作用域最重要的特征是它的定义过程发生在代码的书写阶段。
换句话说,词法作用域是由你在写代码时将变量和块作用域写在哪里来决定的,因此当词法分析器处理代码时会保持作用域不变(大部分情况下是这样的)。
动态作用域
动态作用域是在运行时确定的,关注函数从何处调用 。
动态作用域并不关心函数和作用域是如何声明以及在何处声明的,只关心它们 从何处调用 。换句话说,作用域链是基于调用栈的,而不是代码中的作用域嵌套。
注:
实际上动态作用域是 JavaScript 另一个重要机制 this 的表亲