你不知道的JavaScript上卷
JavaScript和Java的关系就像Carnival和Car的关系一样,八竿子打不着。
JavaScript易上手,但由于其本身的特殊性,相比其他语言能真正掌握JavaScript的人比较少。JavaScript语言本质上有许多复杂的概念,但却用一种看起来比较简单的方式体现出来,比如回调函数。JavaScript开发者只是简单使用这些特性而不会去关心语言内部的实现原理。
阿喀琉斯之踵
不要满足于只是让代码正常工作,而是弄清楚为什么,拥抱整个JavaScript
知其然,也要知其所以然
代码示例、练习题
- 本书第一部分"作用域和闭包"
- 本书第二部分"this和对象原型"
Safari Books Online 数字图书馆,同时以图书和视频的形式出版世界顶级技术和商务作家的专业作品。
第1章 作用域是什么
需要设计一套设计良好的规则来存储变量,并且在之后可以方便的找到这些变量。这套规则叫作用域。
源代码-->>编译-->>执行
编译:
-
分词/词法分析 Tokenizing/Lexing
将由字符组成的字符串分解成有意义的代码块(于该门语言而言),这些代码块叫作词法单元token。var a = 2; ==>> var、a、=、2、;。空格是否会被当成词法单元取决于空格在该门语言中的意义。
分词tokenizing和词法分析lexing之间的细微区别:
差异在于词法单元的识别是通过有状态还是无状态的方式进行(换句话说,如果词法单元生成器在判断a是一个独立的词法单元还是其他词法单元的一部分时,调用的是有状态的解析规则,那这个过程就叫作词法分析) -
解析/语法分析 Parsing
将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表程序语法结构的树。抽象语法树Abstract Syntax Tree,AST。var a = 2;的抽象语法树中可能有一个叫作VariableDeclaration的顶级节点,然后是一个叫作Identifier(它的值是a)的子节点,以及一个叫作AssignmentExpression的子节点。AssignmentExpression节点有一个叫作NumericLiteral(值是2)的子节点。
-
代码生成
将AST转换成可执行代码。与语言、目标平台相关。就是用某种方法将var a = 2;的AST转化为一组机器指令,用来创建一个叫作a的变量(包括分配类存),并将一个值储存在a中。
这是引擎如何管理系统资源的内容
JavaScript引擎在语法分析、代码生成阶段有特定的步骤来对运行性能进行优化,包括对冗余元素进行优化等。JavaScript任何的代码片段的编译过程发生在代码执行前的几微秒内,JavaScript引擎用尽各种办法(比如JIT,可以延迟编译甚至实施重编译)来保证性能最佳。比如JavaScript编译器首先会对var a = 2;进行编译然后作好执行它的准备。
引擎 从头到尾负责整个JavaScript程序的编译及执行
编译器 负责语法分析、代码生成等脏活累活
作用域 负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限
var a = 2;
引擎认为这里有两个完全不同的声明:一个由编译器在编译时处理,另一个由引擎在运行时处理。
上面三者协作的过程:编译器分解成词法单元并将其解析成一个树结构 ==>> 代码生成
代码生成过程:遇到var a,编译器询问作用域是否已经有一个该名称的变量存在于同一个作用域的集合中。是 ==>> 编译器跳过该声明;否 ==>> 要求作用域在当前作用域集合声明一个新变量a。为引擎生成运行时所需代码,这些代码用于处理a = 2这个赋值操作。引擎运行时会首先询问作用域,在当前作用域集合中是否存在a变量,是 ==>> 引擎使用该变量;否 ==>> 引擎会继续查找该变量。
如果引擎找到了a变量,会将2赋值给它。否则引擎抛出异常。
引擎查询变量的方式: LHS查询 RHS查询
LHS查询:变量出现在赋值操作的左侧。试图找到变量容器本身从而对其赋值。a = 2
RHS查询:变量出现在赋值操作的右侧。简单地查找某个变量的值。console.log(a)
既有LHS又有RHS:
function foo(a) { // 形参a
console.log(a); // 2
}
foo(2); // 隐式的a=2操作
作用域嵌套 一个块或函数嵌套在另一个块或函数中。在当前作用域中无法找到某个变量时,引擎就在外层嵌套的作用域中继续找,直到找到或抵达最外层作用域(全局作用域)为止。
RHS查询不到所需变量,引擎抛出ReferenceError异常
LHS查询时,如果在全局作用域也无法找到目标变量,在全局作用域中就会创建一个该名称的变量,并将其返还给引擎。(前提是程序运行在非"严格模式"下,hack做法严格禁止自己使用),在严格模式下,引擎也是抛出"ReferenceError异常"
RHS查询找到一个变量,但你尝试对这个变量的值进行不合理操作,比如试图对一个非函数类型的值进行函数调用,或者引用null或undefined类型的值的属性,引擎会抛出另一种类型异常TypeError
- ReferenceError 同作用域判别失败相关
- TypeError 代表作用域判别成功但对结果的操作非法或不合理