JavaScript引擎的两大步骤:
- 预编译(第一次/前置扫描) — 变量 函数等声明
- 解释执行(第二次扫描)
预编译 - 脚本
脚本:
-
创建全局对象GO(Global Object)
-
加载脚本文件 预编译:
-
找出所有的变量声明,按照变量名加入全局对象,如果已经存在,忽略。
-
找出所有的函数声明,按照函数名加入全局对象,如果已经存在同名变量或者函数,替换。
解释执行
脚本的预编译
1.如果遇到没有var的变量,都不是变量声明,全部都认为是局部变量,不参与预编译。
示例代码:
console.log(aa);
aa = 5;
console.log(aa);
2.即使aa在函数中,aa也是全局变量,是运行时生效,不是预编译时生效。
console.log(aa);
test();
function test(){
aa = 5;
// console.log('Hello!');
}
console.log(aa);
3.脚本中,所有的变量声明,在脚本的预编译阶段完成,所有的变量声明与实际的书写位置无关。
console.log(a); // undefined
var a = 5;
console.log(a); // 5
4.脚本中,所有的函数声明,在脚本的预编译阶段完成,所有的函数声明与实际的书写位置无关。
console.log(f);
function f(){
console.log('哈哈');
}
5.脚本中,如果变量与函数同名,那么函数将覆盖变量
console.log(f); // 输出函数
var f = 123;
console.log(f); // 输出变量 被赋值影响
function f(){
console.log('哈哈哈');
}
console.log(f); // 输出变量
6.脚本中,如果变量与函数同名,函数可以覆盖变量,变量无法覆盖函数。
console.log(f); // 输出函数
function f(){
console.log('hahha');
}
var f = 123;
7.脚本中,如果有多个函数同名,最后声明的函数将覆盖所有前面的同名函数声明。并且,参数个数是忽略的,也就是说,JS压根不支持重载。
console.log(f);
function f(x){
console.log('haha1')
}
function f(x,y){
console.log('haha2')
}
预编译 - 函数调用
函数调用:
创建活动对象AO(Active Object)
预编译:
- scope chain
- 初始化arguments
- 初始化形参,将arguments中的值赋值给形参
- 找出所有的变量声明,按照变量名加入AO,如果已经存在,忽略。
- 找出所有的函数声明,按照函数名加入AO,如果已经存在同名变量或者函数,替换。
解释执行
8.函数中,所有变量声明,在函数的预编译阶段完成,所有变量的声明与实际的书写位置无关
function f(){
console.log(a);
var a = 25;
console.log(a)
}
f();
9.函数中,所有函数声明,在函数的预编译阶段完成,所有函数的声明与实际的书写位置无关。
function f(){
console.log(fin);
function fin(){
console.log('hahha');
}
}
f();
10.函数中,如果变量与函数同名,那么函数将覆盖变量
function f(){
console.log(fin);
var fin = 123;
console.log(fin);
function fin(){
console.log('haha');
}
}
f();
11.函数中,只有函数能够覆盖变量,变量无法覆盖函数
function f(){
console.log(fin);
function fin(){
console.log('haha');
}
var fin = 123;
}
f();
12.函数中,如果有多个函数同名,最后声明的函数将覆盖所有前面的同名函数声明。并且,参数个数是忽略的,也就是说,JS压根不支持重载。
function f(){
console.log(fin);
function fin(x){
console.log('haha1');
}
function fin(x){
console.log('haha2');
}
}
f();
13.当函数预编译后,遇到需要访问的变量或者函数,优先考虑自己AO中定义的变量和函数。如果找不到,才会在其定义的上一层AO中寻找,直至到GO,再找不到才报错。
var scope = 'global';
function f(){
console.log(1,scope); // undefined
var scope = 'local';
console.log(2,scope); // 'local'
}
f();
console.log(3,scope); // 'global'
JavaScript的预编译是个比较重要的知识点,大家要结合代码深入理解一下。