JS运行三部曲(预编译)
JS运行的三个步骤:
- 语法分析
- 预编译
- 解释执行
语法分析:通俗来说就是通篇检查你的代码有没有语法错误,有语法错误的话,程序是不会执行的
解释执行:也就是程序读一句执行一句
最重点的也就是预编译了,那么预编译到底是什么?它发什么在什么时候?
先来段代码压压惊
function fn (a) { console.log(a) var a = 123; console.log(a) function a () {} console.log(a) console.log(b); var b = function () {} console.log(b); function d () {} console.log(d) } fn(1)
这是打印结果
ƒ a() {} 123 123 undefined ƒ () {} ƒ d() {}
是不是有些地方有点懵逼和意外,其实这就是预编译捣的鬼,预编译就是在函数执行之前,在你的内存中申请一点空间,存放变量和函数
下面说说预编译到底是怎样的过程,也就是上面函数到底发生了什么
预编译过程:
- 创建AO对象(Active Object)
- 找形参和变量声明,将形参和变量声明作为AO对象的属性名,值为undefined
- 将实参与形参统一(把实参值赋予形参)
- 在函数体里找函数声明,把函数声明也作为AO对象的属性名,值为函数体()
上面那段代码,fn函数执行之前,先发生预编译过程
第一步
AO = { }
第二步
AO = {
a:undefined,
b:undefined
}
第三步
AO = { a:1, b:undefined }
第四步
AO = { a:function (){}, b:undefined, d:function (){} }
第四步代表预编译完成,然后函数执行
AO = { a:123, b:function (){}, d:function (){} }
function fn (a) { console.log(a) //这时函数未执行,打印function a(){} var a = 123; //此时执行了a=123 console.log(a) //打印123 function a() {} console.log(a) //打印123 console.log(b); //此时函数未执行,打印undefined var b = function () {} console.log(b); //此时执行了 b=function(){},打印function(){} function d () {} console.log(d) //打印function d(){}} } fn(1)
举几个‘栗子’
//1. function fn(a,b) { console.log(a); //打印1 c = 0; var c; console.log(b); //打印function b() {} a = 3; b = 2; console.log(b); //打印2 function b() {}; function d() {}; console.log(b) //打印2 } fn(1);
//2. console.log(bar()); //打印function foo() { //body } function bar(a,b) { return foo; foo = 10; function foo(){ //body } var foo = 11; }
//3. console.log(bar()); //打印11 function bar(a,b) { foo = 10; function foo(){ //body } var foo = 11; return foo; }
以上是单个函数发生的预编译和函数执行过程,那么在<script></script>整个脚本中,是怎么样的呢?
<script type="text/javascript"> var a = 1; function b () { var x = 3; function d () {} } var c = function () { var y = 4; } </script>
同样的过程,在语法分析之后,开始预编译,不过此时预编译创建的叫GO对象,(Global Object)
GO/window = { a:undefined, c:undefined, b:function b () { var x = 3; function d () {} } }
函数执行:
GO/window = { a: 1, c: function () { var y = 4; }, b: function b () { var x = 3; function d () {} } }
在执行b函数之前,将b函数预编译,也就是创建b函数的AO对象
如果有两个<script></script>呢?那就先执行完一个,再执行下一个。
注意问题:
预编译只有变量声明、函数声明,并不会发生赋值,赋值发生在执行阶段
匿名函数function (){ } 不参与预编译
如果你在很年轻的时候,就遭受到了失败,一定要把它当作老天送你的礼物。如果等到四十岁再失败,你会经受不起的。为什么年纪越大,走路越小心,因为越来越经不起跌倒了。