利用空余时间读完这三本小黄书,顺手写写笔记与感想,一定有很大的收获。
第一章 作用域是什么?
还没读几行,一个观点狠狠的冲击着我的认知。
尽管通常将JavaScript归类为“动态”或“解释执行”语言,但事实上它是一门编译语言。
???
我寻思着JS不是解释性语言吗?缓缓打出三问号,继续捋。
编译语言VS解释性语言(个人理解)
- 编译语言:编译器先将编程语言转换成中间文件(如java的.class的文件),再将它转换成机器语言。
- 解释性语言:直接将变成语言转换成机器语言,但是依赖解释器。解释一行,执行一行。
我还是没搞明白
于是我参考到了这篇文章: baijiahao.baidu.com/s?id=159152…
但是,我还是偏向解释性语言...
关于文章里面的知识点,咱慢慢捋。
编译语言中,一段源代码在执行前到底经历了什么?
- 分词/词法分析
var a = 'trio'; 分解成 var、a、=、'trio'、;
- 语法分析
将上一步的成果词法单元转换成一颗抽象语法树(AST)
- 代码生成
将AST转换成机器能识别的代码
复杂吗?书中指出JavaScript引擎要比那些编译过程只有三个步骤的语言的编译器,要复杂的多。
对于JavaScript来说,大多数编译发生在代码执行前的那一刻!而参与JavaScript工作的有三个重量嘉宾:
- 引擎
从头到尾负责整个程序的编译及执行
- 编译器
语法分析、代码生成
- 作用域
负责收集并维护由所有声明的标识符组成的一系列查询
模拟一下
var a = 2;
// 我们都知道 上述代码相当于
var a; // ①
a = 2; // ②
引擎也是这么认为的
其中①是编译器在编译时干的活,②是引擎自己的工作。
编译器会向作用域确定受否有这个变量,是否需要再次声明。而引擎则会执行赋值操作。
深层次解释变量提升
JS引擎在代码执行的前一刻会对其进行编译(比如 var a = 2; var a 部分直接交给了编译器),而编译器会告诉作用域,去帮我创建一个a变量,但是没有值。所以编译的过程,也就是变量提升的过程。
LHS和LRS
number = 1; // ①
console.log(number); // ②
这两行代码,引擎都需要去问作用域查询number这个变量是否存在以及是否有值。但是我们细品一下,他们之间有所不同。
①要找到number这个变量,根本不在乎你有没有值,我在乎的是你这个玩意儿有没有。(LHS)
②要找number这个变量,我要彻彻底底的取到你这个值,传入我的log函数。(RHS)
考虑以下代码
function foo(a){
console.log(a)
}
foo(2)
执行foo的时候,对foo进行RHS。隐式的a=2,对a进行LHS。对a进行RHS,传入log函数。那console.log是个啥玩意儿?引擎又需要对console这个对象进行RHS。
之前读过这部分,所以接受的很快。做个题。
function foo(a){
var b = a;
return a + b;
}
var c = foo(2)
执行var c = foo(2),对c的LHS查询,对foo的RHS查询。传入参数2,对a的LHS查询,执行 b = a,对b的LHS查询,对a的RHS查询,return a + b ,对a、b分别RHS。
区分LHS和RHS的必要性
a = 1; //不报错(非严格模式) LHS查询
console.log(a) // ReferenceError a is not defined RHS查询
我们可以看出:
- 如果RHS查询在所有嵌套的作用域中都没有答案,直接报错。
- 如果LHS查询在全局作用域中也无法找到目标变量,则全局作用域就会友好的为引擎创建一个。
这也可以从更深的角度出发解释为什么都是对变量的查询,一个报错,一个不报错。