函数的执行顺序 1.语法分析(扫描一遍,看有没有语法错误之类的) 2.预编译 3.解释执行(解释一行,执行一行)
预编译和执行是分不开的,预编译就发生在函数执行的前一刻。预编译完成,马上逐行解释执行。下面讲预编译,里边含有解释执行的东西。 先铺垫几个知识点 函数声明整体提升 变量,声明提升 暗示全局变量,任何变量如果未经声明就赋值,此变量归全局所有。一切声明的全局变量,全是window的属性,比如b=3;意思就是window.b=3
变量,声明提升
var a=123相当于,var a;a=123;变量,
声明提升,把var a提升了,打印的话就是undefined
预编译四部曲: 1.创建AO对象 2.找形参和变量声明,将形参名和变量名作为AO属性名,增加到AO里面,并且赋值undefined 3.将实参赋值给形参 4.在函数体里找函数声明,值赋予函数体
第一步: 生成一个活动对象(Active Object),简称AO,又叫做执行期上下文 函数在运行前,会执行词法分析
第二步: 分析形参和变量声明,如var age;或var age=25; AO.age=undefine
第三步: 将实参赋值给形参,如果实参age=30,那么AO里面也跟着变化,这时候 AO.age=30
第四部: 分析函数的声明,如果有function age(){} 把函数赋给AO.age ,覆盖上一步分析的值
下面是一个例子,详细讲述函数执行三部曲,AO执行四部曲的
<script>
function fn(a) {
console.log(a);
var a=123; //相当于var a并且赋值为123
console.log(a);//第三行
function a() { } //这个叫函数声明
console.log(a);
var b=function () { } //这个叫函数表达式,是b的声明赋值
console.log(b);
function d() { } //这个叫函数声明
}
fn(1);
//首先进行语法扫描分析,没发现语法错误,就开始预编译,然后逐行解释执行
//下面重点讲预编译四部曲
/*1.创建AO对象
2.找形参和变量声明,将形参名和变量名作为AO属性名,
增加到AO里面,并且赋值undefined,这时候
AO:{
a:undefine,(形参是a,变量声明也有var a,那就只留一个)
b: undefine,(变量声明var a和var b)
}
3.将实参赋值给形参,这时候
AO:{
a:undefine—>1,(实参a是1,这时候a=1)
b: undefine
}
4.分析函数的声明,如果有同名的就覆盖,因为函数声明优先级最高,这时候
AO:{
a:undefine—>1—>function a() { },(有同名的函数声明,就覆盖)
b: undefine,(b不变)
d:function d() { } (增加了一个d,属于函数声明)
}
下面进入解释一行,执行一行的阶段,已经看过的就不再看了(比如var a)
第一行:console.log(a);从AO里边找,打印出函数体
第二行,var a已经不看了,只剩下赋值123了,这时候AO有变化
AO:{
a:undefine—>1—>function a() { }—>123,
b: undefine,
d:function d() { }
}
第三行:console.log(a)打印出123
第四行:也不看了,之前已经提升过了
第五行:console.log(a)也打印出123
第六行:var b就不看了,只是把b赋值为函数体,这时候又修改AO
AO:{
a:undefine—>1—>function a() { }—>123,
b: undefine—>function () { },
d:function d() { }
}
第七行:console.log(b);打印出函数体function () { }
第八行:不看了,之前提升过了。
视频教程可以参考渡一教育递归,预编译(上)
*/
</script>
预编译不仅发生在函数内,也发生在全局