墨言妹带你细读《你不知道的
JavaScript
》系列的世界,深刻JavaScript
语言内部,弄清楚JavaScript
每个零部件的用途,知其然更要知其因此然。git
一般,把 JavaScript
归类为 “ 动态 ” 或 “ 解释执行 ” 的语言,可是事实上它是一门 编译语言,不提早编译,编译结果也不在分布式系统中进行移植。github
JavaScript 引擎进行编译的步骤和传统的编译语言很是类似,在某些环节比它要复杂。 编程
传统编译语言,在执行以前的三个步骤,统称为 “ 编译 ” 。数组
分词/词法分析( Tokenizing/Lexing
)bash
将有字符组成的字符串分解成(对编程语言来讲)有意义的代码块,这些代码块被称为词法单元( token
)。编程语言
var a = 2;
复制代码
被分解成词法单元:var
、a
、 =
、2
、;
。空格在该语言中有意义,则会被当作词法单元,不然不是。分布式
解析/语法分析( Parsing
)函数
将词法单元流(数组)转换成一个由元素逐级嵌套所组成的表明了程序语法结构的 “ 抽象语法树 ”( Abstract Syntax Tree ,
AST
)。post
var a = 2;
复制代码
以上代码的抽象语法树以下所示:性能
VariableDeclaration
顶级节点
Identifer
子节点,值为 a
AssignmentExpression
子节点
NumericLiteral
子节点,值为 2
代码生成
将 AST
转换为可执代码的过程被称为代码生成。这个过程与语言、目标平台等相关。
即经过某种方法,将 var a = 2 ;
的 AST
转化为一组机器指令,用来建立一个变量 a
,并将值存储在 a
中。
引擎,能够根据须要建立并存储变量。
JavaScript
程序的编译及执行过程。JavaScript
引擎是如何处理 JavaScript
代码的?
好比 var a = 2;
存在2个不一样的声明,变量的赋值操做会执行两个动做。
var a
,在编译阶段,编译器先询问当前做用域,在做用域集合中是否存在变量 a
,若不存在则声明一个新变量名为 a
;接着编译器会为引擎生成运行时所需的代码,处理 a = 2
这个赋值操做。a = 2
),在执行阶段,引擎运行时先询问做用域,在做用域中查找该变量 a
,若是找到就将值 2
赋值给变量 a
,不然引擎就会举手示意并抛出一个异常。如何理解引擎、编译器、做用域的关系
a
来判断它是否已经声明过。JavaScript
引擎的性能要求很高。引擎查找的两种方式: RHS
和 LHS
LHS
查询(左侧):找到变量的容器自己,而后对其赋值,即赋值操做的目标是谁。好比 a = 2;
,为 = 2
这个赋值操做找到一个目标。RHS
查询(非左侧):查找某个变量的值,理解为 retrieve his source value
,即谁是赋值操做的源头。好比: console.log( a );
,须要获取到变量 a
的值,则对变量 a
的 RHS
查询,并传值给 console.log(...)
。function foo(a){
console.log( a ); //2
}
foo(2);
复制代码
上述代码共有1处 LHS
查询,3处 RHS
查询。
LHS
查询有:
a = 2
中,在 2
被当作参数传递给 foo(...)
函数时,须要对参数 a
进行 LHS
查询RHS
查询有:
foo(...)
函数的调用须要对 foo
进行 RHS
查询,意味着 “去找到 foo
的值,并把它给我 ” ,而且 (...)
意味着 foo
的值须要被执行,所以它最好真的示意函数类型的值。console.log( a );
中对 a
进行 RHS
查询,而且将获得的值传给了 console.log(...)
。console.log(...)
自己对 console
对象进行 RHS
查询,而且检查获得的值中是否有一个叫做 log
的方法。做用域是一套规则,用于肯定在何处以及如何查找变量(标识符)。当一个块或函数嵌套在另外一个块或函数中时,就发生了做用域的嵌套。
LHS
查询;若是目的是获取变量的值,就会使用 RHS
查询。LHS
查询。=
操做符或调用函数时传入参数的操做都会致使关联做用域的赋值操做。遍历嵌套做用域链的规则: 引擎从当前的执行做用域开始查找变量,若是找不到,就向上一级继续查找。当抵达最外层的全局做用域时,不管找到仍是没找到,查找过程都会中止。
为何区分 LHS
和 RHS
?变量尚未声明(在任何做用域中都没法找到该变量)的状况下,这两种查询的行为是不同的。
function foo(a){
console.log(a + b);
b = a;
}
foo(2);
复制代码
对一个 “未声明 ” 的变量 b
进行 RHS
查询时,在任何相关的做用域中都没法找到它。
ReferenceError
和做用域判别失败相关,而 TypeError
则表明做用域判别成功了,可是对结果的操做是非法或不合理的。
RHS
查询在做用域链中搜索不到所需的变量,引擎会抛出 ReferenceError
异常。LHS
查询在做用域链中搜索不到所需的变量,全局做用域中会建立一个具备该名称的变量并返还给引擎。ES5
开始,禁止自动或隐式地建立全局变量), LHS
查询失败并不会建立并返回一个全局变量,引擎会抛出同 RHS
查询失败时相似的 ReferenceError
异常。RHS
查询成功状况下,对变量进行不合理的操做,引擎会抛出 TypeError
异常。(好比对非函数类型的值进行函数调用,或者引用 null
或 undefined
类型的值中的属性)。最后, 书读百遍其义自见,抱着以教为学的初衷,不断反思、刻意练习,若对你有帮助,请点个赞,谢谢您的支持与指教。
参考文献: 木易杨博客
历史文章: 【译】30 Seconds of ES6 (一)