本文是前端学习笔记中的第三篇,对应的是渡一教育的web前端开发JavaScript精英课js中的第九课时,这一课时讲的相当好,由浅入深,把预编译的过程都讲的极度透彻,如果对预编译不太熟悉的最好还是去看一看
JS运行三部曲
预编译
JS运行时一共分三步
(1)语法分析
JS运行前会先把JS代码通篇扫描一遍,看有没有低级的语法错误,比如少了个分号,或者多了些不该有的字符什么的
(2)预编译
本文的重点,后面会详细介绍,这里先放两句总结性结论
函数声明 --- 整体提升
也就是说,函数声明后,系统会把整个函数放到当前script标签的开头处(这种说法并不严谨,仅仅是帮助理解)
变量 --- 声明提升
注意,是变量 --- 声明提升,也就是说var a = 123; 系统会将var a;放到当前script标签的开头处(这种说法并不严谨,仅仅是帮助理解),但是a此时是undefined的,因为仅仅是声明提升,赋值操作并没有执行
(3)解释执行
解释型语言的特点,不通篇编译,而是解释一行执行一行
预编译发生在函数执行前一刻
准确来说,预编译一共有两种,一种是相对于调用函数而言的预编译,一种是在全局中的预编译
先说函数中的预编译过程
为方便理解,先看一个demo
根据上面提到的预编译步骤,函数执行前
AO {
a : undefined
b : undefined
}
AO {
a : 1
b : undefined
}
AO {
a : function a ( ){ }
b : undefined
d : function d ( ) { }
}
到此,预编译过程便结束了,接着开始正式执行函数体内容,
console.log(a); // function a ( ) { }
a = 123; // 注意之前预编译的步骤仅仅是声明了,并没有赋值
此时AO对象的属性被修改
a : 123
b : undefined
d : function d ( ) { }
}
console.log(a); // 123
// function a ( ) { } // 本行被跳过,因为在预编译环节已执行
console.log(a); // 123
b = function ( ) { } // 之前预编译步骤已声明,这里仅仅是赋值
此时AO对象的属性被修改
a : 123
b : function ( ) { }
d : function d ( ) { }
}
console.log(b); // function ( ) { }
// function d( ) { } // 不会执行,预编译过程已经执行过
此时,整个函数从预编译到执行环节便完整结束了
接下来,我们再来说说全局中的预编译过程,其实和函数的预编译过程极其相似,只是少了一步
这里就不再赘述了,原理都是一样的
值得一提的,未经声明的变量是归全局变量所有的
var a = b =10;的过程:先把10赋给了未经声明的b,再把b的值赋给了a,那么此时b便提升为全局变量,如今在函数test外可以通过b或window.b直接访问这个全局变量
还有最后一点,预编译过程无视if() {} 之类的判断语句,因为此时根本就还没执行函数,若if语句中有变量声明/函数声明是完全没问题的,是起作用的