——写在前面:此贴为笔者在消化了网上前辈们的无私分享后,经自己的理解,然后总结的记录贴。主要目的在于加深自己的理解与记忆,同时也希望能帮助到需要的人。
在正式介绍JS运行机制之前,请先花上几分钟理解下如下几个概念:一、代码块;二、声明式函数与赋值式函数;三、预编译及执行;
一、代码块 :JS中代码块指的是由<script></script>标签分割的代码段,例如下:
<script>
alert('这是代码块一'); //执行‘这是代码块一’
</script>
<script>
alert('这是代码块二') //执行‘这是代码块二’
</script>
JS是按照代码块来进行编译和执行的,代码块间相互独立,但是变量和方法共享,例如下:
<script>
var test='我是代码块一的变量';
alert(str); //因为没有定义str,所以浏览器会出错,代码块运行到此结束
alert('我是代码块一'); //没有被执行
</script>
<script>
alert('这是代码块二'); //弹出‘我是代码块二’
alert(test); //弹出‘我是代码块一’
</script>
上述例子中,代码块一运行报错,但是不影响代码块二的运行,这就是代码块间的独立性,代码块二中成功调用了代码块一中的变量test则说明了代码块间变量的共享。
二、声明式函数及赋值式函数:JS中的函数定义分为声明式及赋值式两种。
<script>
function func(){
//我是声明式函数
}
var func_=function(){
//我是赋值式函数
}
</script>
声明式函数与赋值式函数的区别在于:在JS的预编译期,声明式函数会被先提取出来(而赋值式函数仅仅声明被提取出来),然后再按顺序执行JS代码。
例如下:
<script>
document.write(typeof func); //执行结果:‘undefined’
var func=function(){}
document.write(typeof func); //执行结果:‘function’
</script>
三、预编译期与执行期:JS解析过程的两个阶段
在预编译期,JS会对代码块中的所有声明变量和函数进行处理,但需要注意的是此时处理的函数只是声明式函数,而变量也只是进行了声明但未进行初始化及赋值(参考调用先于声明时产生的undefined)。
<script>
func();
function func(){alert('执行了声明式函数')} //弹出‘执行了声明式函数’
var func=function(){alert('执行了赋值式函数')}
</script>
<script>
func();
function func(){alert('执行了声明式函数一')} //同名函数被后来者覆盖
function func(){alert('执行了声明式函数二')} //弹出‘执行了声明式函数二’
</script>
<script>
alert(str); //报错‘str is not definded’
</script>
<script>
alert(str); //弹出‘undefined’,虽然在预处理过程中对声明进行了处理,但并没有初始化与赋值
var str='我是字符串';
</script>
进过上面的了解后,我们再看看下面一个例子:
<script>
func(); //浏览器报错‘func is not defined’
</script>
<script>
function func(){alert('执行函数func')}
</script>
为什么运行上面的代码会报错呢?按照之前的理解,声明函数应该在预编译期就被进行处理,怎么还是会报错undefined呢?这里需要大家注意的是,前面提到过JS引擎是按照代码块来顺序执行的,其实完整的说法是:按照代码块来进行预处理和执行的,也就是说预处理的只是执行到的代码块的声明函数和变量,而对于还未加载的代码块,是没办法进行预处理的,这也是边编译边处理的核心所在。
总结下JS执行顺序机制:
step 1. 读入第一个代码块。
step 2. 做语法分析,有错则报语法错误(比如括号不匹配等),并跳转到step5。
step 3. 对var变量和function定义做“预编译处理”(永远不会报错的,因为只解析正确的声明)。
step 4. 执行代码段,有错则报错(比如变量未定义)。
step 5. 如果还有下一个代码段,则读入下一个代码段,重复step2。
step6. 结束。
笔者对于顺序机制的理解并不完全,仅从现有的例子来看,该顺序并无太大问题,但是如果上述顺序有错误的地方还希望多多指正~