javaScript 作用域——作用域是什么?

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第 3 天,点击查看活动详情

javaScript 作用域——作用域是什么?

编译原理

大部分编程语言的基本功能之一,就是能够存储变量当做值,并且能对这个值进行修改和删除。正是这种存储和访问变量的值的能力将状态带给了程序。但是,我们不禁要问了,这些状态变量存储在哪里?他们是怎么样被找到的?

传统的编译流程一般分为三个阶段:

  • 词法解析

    这个过程会将由字符组成的字符串解析成有意义的代码块,这些代码块被称为词法单元,比如说:var a = 1; var b = 2;会被解析成var, a, =,2,;,var,b,=,2,;这些词法单元。至于空格是否会被解析,则看空格在这门编程语言当中是否有特殊意义。

  • 语法解析

    词法解析完成后,会生成一个数组,也就是词法单元流,这个数组会被转换成一个有元素逐级嵌套组成的树形结构,而这个树形结构被称为抽象语法树var a = 2;解析成抽象语法树,会有一个顶级节点VarableDeclaration,接下来是一个叫作Identifier(它的值是 2)的子节点,以及一个叫作AssignmentExpression的子节点。AssignmentExpression节点有一个叫作NumericLiteral(它的子也为 2)的子节点。

  • 代码生成

代码生成就是将抽象语法树转换为可执行代码的过程。比如将var a = 1;转换成可执行代码简单来讲就是创建了一个变量a,并且为a赋值为 1。

javascript当中的编译比上面的传统编译还要复杂。在 javascript 进行编译的时候,在语法分析和代码生成阶段,需要对运行的性能进行优化,以提高编译的效率。但是很显然,浏览器不会给我们这么多时间来对代码进行优化。对于 javascript 来说,大部分情况下编译发生时间再代码执行的前几毫秒。在这短短的时间里面,编译器会尽可能的对代码进行优化。

简单来说,javascript 在执行代码前会对其进行编译,编译的时机就在执行前的毫秒,甚至更短。

扫描二维码关注公众号,回复: 14208206 查看本文章

理解作用域

想要理解作用域,我们首先要知道代码在执行的过程当中是如何使用作用域的,在 javascript 当中,编译器、作用域、引擎三者之间构成了一个很大的关系。

比如:var a = 2;这句代码,编译器解析,引擎执行的过程会发生什么呢?

首先,编译器会将var a = 2;解析成一个抽象语法树,这个抽象语法树会被转换成可执行代码。在这个过程当中,编译器解析var a的时候,会先去作用域中查看是否已经声明了一个变量名为a 的变量,如果没有,编译器会在作用域当中添加一个变量名为a的变量。接下来,编译器会为引擎生成运行所需要的代码。这些代码被用来处理a = 2,在引擎执行的之后, 会先询问作用域当中是否包含一个变量名为a的变量,如果有,则将它赋值为2,如果找到最顶层的作用域都没有找到这个变量,那么引擎会为它在全局作用域中创建一个变量,并且赋值为2

LHS 查询和 RHS 查询

在编译器对var a = 2;进行解析的时候,会进行两个查询操作,分别为LHSRHS。当对var a进行作用域查找的时候会进行LHS查询,而对a = 2进行查询的时候会进行RHS查询。 简单来说LHS查询是对变量在声明时候执行的查询操作,RHS查询在变量赋值的时候执行的查询操作。更准确一点,RHS 查询与简单的查找某一个变量的值别无二致,而 LHS 查询则是找到 变量容器本身,从而对其进行操作。

console.log(a);
复制代码

在上面的代码当中,a的引用是一个 RHS 引用。

a = 2;
复制代码

这里对a进行赋值是一个 LHS 引用。因为我们需要获取a容器的本身,对其进行赋值操作。

作用域嵌套

我们前面说过,作用域是根据名称查找变量的一套规则。在实际情况当中,通常需要同时对多个作用域进行查找。

当一个作用域嵌套在另一个函数/块作用域当中的时候,就发生了作用域嵌套。所以,当在当前作用域当中无法找到某个变量的时候,就会向外寻找,请看下面的例子

var c = 2;

function foo() {
  function bar() {
    var c = 5;
    function baz() {
      console.log(c);
    }

    baz();
  }

  bar();
}

foo(); // 5
复制代码

上面的代码当中,baz嵌套早bar当中,而bar又放在foo当中。当我们需要在baz当中获取变量c的时候,会先在baz的作用域当中查询,如果没有找到,就向 上一层作用域查找,最终在bar的函数作用域当中找到了。

猜你喜欢

转载自juejin.im/post/7102730175086854151