众所周知,js代码在执行过程中,首先会经历以下几个基本步骤:
1. js引擎会通篇地检查所有代码,顺序依次从上而下,目的是检查代码中是否出现了基本的语法错误或逻辑错误。(出现错误立即停止,及报错)
2.检查无误,开始预解析。
3.等待运行代码块。
其中预解析是整个程序中最为重要的一个环节。这里我们详细的进行讲解...
预解析大概又分为以下几个过程:
1.首先在全局作用域(相当于window)下先隐式地生成GO(globle Object)对象,将全局作用域下的变量声明和函数声明作为GO对象的属性名,属性值赋予其对应的值,比如:
<script>
var a = 1;
function b() {}
b();
</script>
这时生成的GO对象为:
GO {
a: 1,
b: function b() {}
}
2.在生成GO后,又开始对函数进行预解析,(没错函数也要被预解析)当js引擎开始执行函数时,会在函数的内部,执行的前一刻生成一个AO(Acitveble Object)对象(即执行期上下文)并处于函数作用域的最顶部,但在函数执行完毕后会立即销毁AO,防止内存堆积。
AO生成过程四部曲:
1.先局部(在函数作用域内)创建AO对象.
2.将函数的形参 、变量声明中的形参名和变量名作为AO的属性名,值为undefined.
3.将实参值赋予形参.
4.在函数体里找函数声明,将函数名作为AO的属性名,值为函数体.
比如下面的例子:
<script>
funciton add(n) {
var a = 2;
var b = 3;
funciton test(){}
function demo(){}
console.log(a);
console.log(b);
console.log(test);
console.log(demo);
}
add(1);
</script>
函数add的 AO 生成过程:
1.先局部(在函数作用域内)创建AO对象.
2.将函数的形参 、变量声明中的形参名和变量名作为AO的属性名,值为undefined.
AO {
n:undefined,
a:undefined,
b:undefined
}
3.将实参值赋予形参.
AO {
n:1;
a:undefined,
b:undefined
}
4.在函数体里找函数声明,将函数名作为AO的属性名,值为函数体.
AO {
n:1,
a:undefined,
b:undefined,
test:funciton test(){},
demo:function demo(){}
}
完成预解析后,函数开始等待被执行。在执行过程中AO的属性值会按函数内部执行顺序从上而下的进行覆盖,也就是同名不同值的会按先后顺序被覆盖,这时的AO为:
<script>
AO {
n:1,
a:2,
b:3,
test:funciton test(){},
demo:function demo(){}}
此时的函数体就相当于:
funciton add(n) {
a = 2; (此时变量已经提升上去)
b = 3;
funciton test(){}(函数声明也提升了)
function demo(){}console.log(a); 2
console.log(b); 3
console.log(test); funciton test(){}
console.log(demo); function demo(){}
}
c(1);
</script>
为了方便家人们更好地理解函数预解析过程,我会在下一篇文章中演示几道经典题目。由于是自己个人的理解,再加上自己文笔不太好,文章中有些地方可能有表述不清的地方,希望家人们能提出来。非常感谢大家能浏览我的文章~