作用域的概念现代编程语言最基本的功能之一就是能够将值存储在变量中以供以后使用和修改。也正是这个函数为程序带来了状态。在JavaScript中,范围是一组精心设计的用于存储变量的规则。编译原理简述通常我们把JavaScript归类为“动态”或“解释型”语言,但它实际上是一种编译型语言。与传统的编译语言不同,它不是提前编译的,编译后的结果也不能跨分布式系统移植。比如V8引擎,为了提高JavaScript代码的运行性能,在运行前先编译成本地机器码,然后执行机器码,达到提高速度的目的。分词/词法分析这是将由字符组成的代码分解为对程序有意义的代码块(称为标记)的过程。例如varfoo='bar'通常会分解为这些标记:var,foo,=,'bar'解析/解析将标记转换为“语法树”,这棵树称为“抽象语法树”(AST).image代码生成将上面的抽象语法树转换为机器可执行的代码对于JavaScript引擎来说要比语言的编译器复杂得多,只需三个步骤。例如,在语法分析和代码生成阶段都有特定的步骤来优化运行性能,包括优化冗余元素。对于JavaScript,大多数情况下编译发生在代码执行的前几微秒,任何代码片段都必须在执行前编译。所以JavaScript编译器先编译varfoo='bar',然后准备执行,一般是马上执行。引擎、编译器、作用域在赋值操作中的配合引擎:负责整个JavaScript程序从头到尾的编译和执行过程编译器:负责语法分析和代码生成作用域:负责收集和维护由一系列查询组成所有变量的代码varfoo='bar'很可能被认为是一个简单的语句。实际上,JavaScript在执行的时候,会分成两条完全不同的语句。编译器首先将此代码分解为词法单元,然后将其解析为树结构。(在下一步的代码生成过程中,这段代码的处理方式将与预期不同)当遇到varfoo时,编译器会检查作用域中是否已经存在同名变量。编译器忽略声明(如果有)并继续编译。否则,它会生成代码在当前作用域的变量集合中声明一个新变量,命名为foo接下来,编译器会为引擎生成运行时代码来处理foo='bar'的赋值。引擎运行时,会先查询当前作用域中是否存在名为foo的变量。如果有引擎,就会使用这个变量,否则它会一直查找上层作用域。如果最终找到变量foo,则将'bar'分配给它,否则抛出异常。总结:变量的赋值会执行两个动作:首先,编译器在当前作用域内声明变量(如果变量还没有被声明);然后运行时引擎在作用域中搜索变量,如果能找到就赋值。LHSqueryvsRHSquery当引擎执行编译器生成的代码时,它会查找foo以确定它是否已经被声明。查找过程由范围协助。在我们的示例中,引擎正在为变量foo执行LHS查找,还有另一种查找类型称为RHS查找。顾名思义,就是Lefthandside和Righthandside的意思LHS:变量出现在赋值操作的左侧(查找赋值操作的目标是谁)RHS:变量出现在其他地方(查找赋值操作的来源)value)//考虑以下代码console.log(foo)本例中foo的引用是RHS查询,这里没有给foo赋值。相反,我们需要先找到foo的值,然后再将其传递给log方法。//相对于foo='bar',这里foo的查询是LHS查询。我们不关心foo的当前值是多少,只是想找到这个赋值操作的目标。//分析下面的代码foo(...)函数调用的最后一行需要对foo执行RHS查询→在查找foo的值作为输入参数时有一个隐含的a='bar',需要执行LHS查询console.log(a)onaandperformRHSqueryonaconsole.log(...)本身也需要RHSqueryscopeforconsoleobjects的嵌套我们在文章开头说过,scope是一组通过名称查找变量的规则。在实践中,需要同时考虑多个范围。当一个块或函数嵌套在另一个块或函数中时,就会发生作用域嵌套。因此,当目标变量在当前作用域中找不到时,会逐层向上查找,直到全局作用域。//考虑以下代码functionfoo(a){console.log(a+b)}varb=258;foo(369)对b的RHS查询不能在foo内部完成,但可以在上一级范围内完成(在本例中为全局范围)。LHS和RHS查询都会逐层搜索范围,直到找到(或到达全局范围)。ReferenceError在上一节提到了LHS,RHS会在作用域内一层层查找变量,但是如果到了全局作用域还是找不到变量怎么办?这时候区分LHS和RHS查询的意义就体现出来了。如果RHS查询在任何嵌套范围内都找不到所需的变量,引擎将抛出ReferenceError。如果LHS查询在任何嵌套范围内都没有找到所需的变量,引擎会在全局范围内创建一个具有该名称的变量并将其返回给引擎。注意:严格模式是在ES5中引入的。与普通模式相比,严格模式的区别之一是全局变量的自动或隐式创建。因此,当严格模式下LHS查询失败时,不会创建和返回全局变量,引擎也会抛出ReferenceError。SummaryScope是一组规则,用于确定查找变量的位置和方式。如果搜索的目的是为变量赋值,则使用LHS查询;如果目的是获取变量的值,将使用RHS查询。JavaScript引擎在执行之前编译代码。在此过程中,像varfoo='bar'这样的语句被分解成两个独立的步骤。1.varfoo在其范围内声明了一个新变量。此操作发生在代码执行之前。2.接下来foo='bar'查找(LHS)变量foo并赋值给它。LHS和RHS查询都在当前执行范围内开始。如果有必要(在当前作用域内没有找到该变量),它会继续在上层作用域中寻找目标变量,直到到达全局作用域,无论找到与否都会停止。不成功的RHS查找将导致抛出ReferenceError,不成功的LHS查找将导致自动和隐式创建全局变量(在非严格模式下),或抛出ReferenceError(在严格模式下)。
