1.编译理论
传统的语言编译过程,会经过三步编译步骤(js的编译过程实际会复杂很多):
1.标记/词法分析:
var a = 2;
会被分解为,var
,a
,=
,2
,;
,空格可能会被标记。2.句法解析
3.产生可执行的代码
但是js并不会像其他语言有大量时间提前编译好。而是在代码执行的微妙级时间前编译,为了保证快速执行,js引擎用了各种各样的技巧。
理解作用域
理解作用域,我们可以想象一个对话过程。以var a = 2;
为例。
对话成员如下:
- 引擎:负责全程编译和程序执行。
- 编译器:引擎的一个朋友。句法解析,生成可执行代码。
- 作用域:引擎的另一个朋友。搜集并维护一个向上查找的变量列表。强制执行一系列严格的规则,保证这些变量按规则被执行。
当我们看到var a = 2;
时,可能觉得这就是一个句子。而引擎看到的却是两个的语句,一个是编译器执行期间的语句,一个是引擎执行期间的语句。
编译器对这个程序要做的第一件事是执行词法分析,将其分解为一个个标记,然后将其解析为词法树,但是,当编译器进入执行代码生成阶段时,它对待这个程序的方式可能与假定的有所不同。
它会做如下事情:
1.当遇到
var a
时,编译器会询问当前作用域集合是否已经存在一个变量a。如果存在,编译器将忽略此声明(var a
)并继续执行。否则,编译器将要求当前作用域集合声明一个名为a的新变量。2.编译器然后生成可执行代码供引擎稍后执行,以处理a = 2语句。运行代码,引擎将首先询问作用域,在当前作用域集合中是否有一个名为a的变量。如果是,js引擎使用该变量。如果没有,js引擎将查找其他地方。
如果最终找到了a变量,那么就会给它赋值为2.
总结:变量赋值分为两步,首先,编译器声明一个变量(当前作用域没有声明变量的情况下),其次,当执行的时候,引擎会从当前作用域向上查找该变量并赋值.
赋值操作的两个部分
一,赋值的目标(变量)是谁?
二,赋值的内容是什么?
举个例子,
console.log(a)
属于第二部分,因为此时没有值赋值给a
的操作.
a = 2
属于第一部分,不管此时a
是什么值,现在给a
赋值为2。
另外一个例子,
function foo(a) {
console.log( a ); // 2
}
foo( 2 );
函数调用阶段,属于赋值的第二部分,向上查找
foo
的值并调用它,(..)意味着foo
的值应该是个函数,且需要马上执行。
其中还包括了,当给函数传参时发生的a = 2
这部分。
执行console.log()
的log
的时候,也属于第二部分,它会向上查找console有没有这个方法。
作用域就是一系列的规则,以决定在哪里以什么样的方式查找变量。查找变量可能是为了给它赋值,也可能是为了获取变量的值。
赋值操作,发生于使用赋值操作符=
或 给函数传参数的时候。
js引擎在执行前会编译代码,编译分为两步,以var a = 2 ;
为例:
1.
var a
在当前作用域顶部声明变量。
2.a = 2
,从当前作用域向上查找变量a
,如果找到了就赋值。