zoukankan      html  css  js  c++  java
  • 作用域是什么《你不知道的JavaScript(上)》

    编译原理

    1.变量存储在哪里?程序如何找到它们?

    这些问题说明需要一套设计良好的规则来存储变量, 并且之后可以方便地找到这些变量,这套规则被称为作用域。 

    2.在传统编译语言的流程中, 程序中的一段源代码在执行之前会经历三个步骤, 统称为“编译”。 

    分词/词法分析

    这个过程会将由字符组成的字符串分解成有意义的代码块, 这些代码块被称为词法单元。

    例如, 考虑程序 var a = 2;。 这段程序通常会被分解成为下面这些词法单元: var、 a、 =、 2 、 ;。

    空格是否会被当作词法单元, 取决于空格在这门语言中是否具有意义。 

    解析/语法分析

    这个过程是将词法单元流(数组) 转换成一个由元素逐级嵌套所组成的代表了程序语法结构的树。 这个树被称为“抽象语法树”。

    var a = 2; 的抽象语法树中可能会有一个叫作 VariableDeclaration 的顶级节点, 接下来是一个叫作 Identifier(它的值是 a) 的子节点,以及一个叫作 AssignmentExpression 的子节点。

    AssignmentExpression 节点有一个叫作 NumericLiteral(它的值是 2) 的子节点。 

    代码生成

    将 AST 转换为可执行代码的过程称被称为代码生成。 这个过程与语言、 目标平台等息息相关。

    抛开具体细节, 简单来说就是有某种方法可以将 var a = 2; 的 AST 转化为一组机器指令, 用来创建一个叫作 a 的变量(包括分配内存等), 并将一个值储存在 a 中。 

    3.任何 JavaScript 代码片段在执行前都要进行编译。 


    理解作用域

    1.引擎

    从头到尾负责整个JavaScript程序的编译及执行过程。

    编译器

    负责语法分析及代码生成

    作用域

    负责收集并维护由所有声明的标识符组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限

    2.var a = 2;

    1)编译器首先会将这段程序分解成词法单元, 然后将词法单元解析成一个树结构。

    但是当编译器开始进行代码生成时, 它对这段程序的处理方式会和预期的有所不同。 

    2)遇到 var a, 编译器会询问作用域是否已经有一个该名称的变量存在于同一个作用域的集合中。

    如果是, 编译器会忽略该声明, 继续进行编译; 否则它会要求作用域在当前作用域的集合中声明一个新的变量, 并命名为 a。 

    3)接下来编译器会为引擎生成运行时所需的代码, 这些代码被用来处理 a = 2 这个赋值操作。

    引擎运行时会首先询问作用域, 在当前的作用域集合中是否存在一个叫作 a 的变量。

    如果是, 引擎就会使用这个变量; 如果否, 引擎会继续查找该变量。 

    3.当变量出现在赋值操作的左侧时进行 LHS 查询, 出现在右侧时进行 RHS 查询。


    作用域嵌套

    当一个块或函数嵌套在另一个块或函数中时, 就发生了作用域的嵌套。

    因此, 在当前作用域中无法找到某个变量时, 引擎就会在外层嵌套的作用域中继续查找, 直到找到该变量,或抵达最外层的作用域(也就是全局作用域) 为止。 


    异常

    1.如果 RHS 查询在所有嵌套的作用域中遍寻不到所需的变量, 引擎就会抛出 ReferenceError 异常。

    相较之下, 当引擎执行 LHS 查询时, 如果在顶层(全局作用域) 中也无法找到目标变量,全局作用域中就会创建一个具有该名称的变量, 并将其返还给引擎, 前提是程序运行在非“严格模式” 下。 

    2.严格模式在行为上有很多不同。

    其中一个不同的行为是严格模式禁止自动或隐式地创建全局变量。  

    3.接下来, 如果 RHS 查询找到了一个变量, 但是你尝试对这个变量的值进行不合理的操作,比如试图对一个非函数类型的值进行函数调用, 或着引用 null 或 undefined 类型的值中的属性, 那么引擎会抛出另外一种类型的异常, 叫作 TypeError。 

    4.ReferenceError 同作用域判别失败相关, 而 TypeError 则代表作用域判别成功了, 但是对结果的操作是非法或不合理的。 


      

    小结

    1.作用域是一套规则, 用于确定在何处以及如何查找变量

    2.如果查找的目的是对变量进行赋值, 那么就会使用 LHS 查询; 如果目的是获取变量的值, 就会使用 RHS 查询。

    3.操作符调用函数时传入参数的操作都会导致关联作用域的赋值操作。 

    4.JavaScript 引擎首先会在代码执行前对其进行编译, 在这个过程中, 像 var a = 2 这样的声明会被分解成两个独立的步骤:

    1)首先, var a 在其作用域中声明新变量。 这会在最开始的阶段, 也就是代码执行前进行。

    2)接下来, a = 2 会查询(LHS 查询) 变量 a 并对其进行赋值。 

    5.不成功的 RHS 引用会导致抛出 ReferenceError 异常。

    不成功的 LHS 引用会导致自动隐式地创建一个全局变量(非严格模式下), 该变量使用 LHS 引用的目标作为标识符, 或者抛出 ReferenceError 异常(严格模式下)。 

  • 相关阅读:
    gsm at 指令
    wm8976 codec
    【Gym 100971J】Robots at Warehouse
    【XDU1144】合并模板
    腾讯云CentOS7安装LNMP+wordpress
    【USACO1.1】Broken Necklace
    【校赛小分队之我们有个女生】训练赛6
    【计导作业】——商品记录
    C 文件读写2
    C 文件读写1
  • 原文地址:https://www.cnblogs.com/linxian95/p/10149943.html
Copyright © 2011-2022 走看看