你不知道的Javascript(上卷)这本书在我看来是一本还不错的书籍,这本书用比较简洁的语言来描述Js的那些”坑”,在这里写一些博客记录一下笔记以便消化吸取。git
1 编译原理github
在此书中,开始便提出:Javascript是一门编译型语言,我一开始觉得这是国内翻译的锅,翻译的不够准确,后来我还专门去github看了,做者确实是将Js描述为一门编译型语言(compiled language)。然而我认为做者更想表达的是Js也拥有和Java通常的特定的编译过程.而不是传统得认为只是单纯的进行”解释执行”。函数
编译型语言的定义是编译型语言的首先将源代码编译生成机器语言,再由机器运行机器语言,就如C/C++通常。性能
Java把源代码编译成为JVM能够执行的字节码,明显是不符合编译型语言的定义的,因此Java是一门解释型语言。而Javascript也拥有同Java相似的过程,可是这个过程很短。这它在代码执行前的几微秒内要完成程序的编译,而后立刻执行。spa
Javascript的编译分为三个步骤:翻译
a.分词/词法分析 对象
b.解析/语法分析 ip
c.代码生成内存
Javascript在编译阶段的语法分析和代码生成阶段使用了各类方法(JIT, 延迟编译, 重编译等)保证性能最佳。作用域
2 理解做用域
书中所说的Javascript的解释和执行的过程,主要就几个角色参与:
引擎:负责编译的第一部分(分词/词法分析)和执行编译后生成的代码
编译器:负责编译的第二部分(语法分析)和编译的第三部分(代码生成)
做用域:负责收集全部声明的标识符,即变量,而且维护一套管理变量的规则
对于var a = 2;能够认为他的执行过程以下
a.引擎执行编译的第一步 分词/词法分析
b.引擎调用编译器,编译器进行编译的语法分析和代码生成
a) 编译器遇到var a,去当前的做用域集合中查找是否存在a这个变量
b) 若是存在,则忽略变量声明
c) 若是不存在,则在做用域中声明一个新的变量,命名为a
d) 生成引擎运行所需代码
c.引擎运行生成的代码
a) 去当前做用域中查找是否存在a变量,若是找不到这个变量,它会继续往上 (它的上一层做用域)寻找
b) 若是存在,会使用这个变量,并为这个变量赋值
c) 若是不存在,会抛出一个异常或者建立一个变量(*详细请看下文LHS和 RHS)
备注:引擎去做用域查找变量的方式有两种一种是LHS查询,另一种是RHS查询。
a) LHS能够认为是表明取得变量的源地址(变量实际上就是计算机内存中一段连续 的地址,而且咱们经过为变量命名来为这段内存地址命名),取得源地址用于为该变量赋值。
b) RHS表明查询获得变量的值,通常用于为其余变量赋值
直接说LHS和RHS确定太抽象,也很差记,LHS 和 RHS 字面上区别在于 L 和 R,分别表明左(left)和右(right)
对于 a= b = 2 这行代码,代码首先为b赋值为2。而后呢?重点来了
1首先查询获得b的值
2把查询的获得的值赋给a
而这,刚好分别对应着RHS查询和LHS查询,首先对处于右边的b进行一次RHS(查询获得b的值),而后LHS查询找到a变量的源地址(为a赋值)。
函数的执行,好比foo(2),实际是对foo进行一次RHS,获得的值是函数类型的对象,而后去执行他时,还有一次(多个形参时就是屡次)对形参的LHS。
3 做用域嵌套
当一个块或函数嵌套在另一个块或函数中时,就发生了做用域的嵌套,所以,若是咱们在一个块中定义函数的话,函数执行过程当中是能够调用到函数外层的变量的。
整个的函数做用域链就像一个金字塔,在书中做者把函数做用域链比喻为一个建筑。在做用域的最顶层是一个全局做用域,每个做用域均可能包含数个子做用域,如此往下延伸,所以金字塔对函数做用域链会更贴切。
LHS和RHS都会先在当前做用域进行查找,若是没有找到结果,那么它们都会一直往上寻找,直至全局做用域。
4 异常
若是引擎在最顶层的全局做用域中也找不到,那么会有什么行为呢? 在这里的话,LHS和RHS会有不一样的行为
a.对于LHS:若是在最顶层也找不到这个变量,若是在非严格模式下,那么会在全局 做用域建立一个全局变量,若是处于严格模式下,严格模式禁止自动或隐式建立全 局变量,此时会抛出ReferenceError异常
b.对于RHS:若是在最顶层也找不到这个变量,那么会抛出ReferenceError异常。若是对查询的变量进行不合理的操做,好比调用非函数对象,会抛出TypeError异常。