一、js运行三部曲:
1.语法分析(通篇扫描看有没有语法错误)
2.预编译
3.解释执行
二、预编译前奏
1、imply global 暗示全局变量:任何变量如果未经声明就赋值,此变量为全局对象所有
eg: a = 123;
var a = b =123;
2、一切声明的全局变量,全是window的属性,一切定义在全局上的变量,都归window所有(window等价于全局)
eg: console.log(a) 等价于 console.log(window.a);
举个例子:
<script> function test(){ var a = b = 123; } test(); console.log(b); </script>
其结果为:123
如果将console.log(b) 改成 console.log(a) 则会出现 “
” 的错误。这是因为执行test()函数时,先对 b赋值,使 b=123, 然后再 var a 进行声明, 最后使a=b,
b在未声明的时候进行赋值,所以为全局变量,可以在函数外访问,而a则是局部变量,不能再函数外访问
三、预编译的两个规则:
a.函数声明整体提升 : 函数声明不管位于哪里,系统总会把声明提升到逻辑的最前面,因此无论函数调用在声明前或声明后结果都一样
b.变量 声明提升:会把变量的声明提升到函数调用前面
举两个小例子:
<script>
test();
functiont test(){
console.log('a');
}
</script>
输出的结果为: a
<script> console.log(a); var a = 123; </script>
输出结果为:undefined;
四、预编译四部曲(函数预编译时)
通过下面例子进行讲解
<script type="text/javascript"> function fn(a) { console.log(a); var a = 123; console.log(a); function a() {} console.log(a); var b = function() {} console.log(b); function d() {} } fn(1);
1.创建AO对象 (Activation object)(执行期上下文)
AO{ }
2:找形参和变量声明,将变量和形参名作为AO()属性名,值为undefined
AO{
a:undefined
b:undefined
}
3.将实参值和形参值统一
AO{
a:1
b:undefined
}
4.在函数体里找函数声明,值赋予函数体
AO{
a:function a(){}
b:undefined
d:function d(){}
}
预编译完后进行执行:
首先一句一句执行,执行第一句console.log(a);那么,会在AO对象中调取a,在AO对象中a等于function a(){},那么就输出function a(){}
然后到达第二句 var a = 123,var a 已经被提升了,所以不用管,直接到a = 123,所以,在AO对象中,a变成了123
AO对象变成123后,再来进行执行第三句,console.log(a);那么,现在a等于123,那么就是输出123,
到达第四句,第四句是函数,由于函数已经被提升了,所以没有这一步,然后再到第五句,第五句是console.(a),所以输出还是123吧
然后再到第六句,第六句是var b = function (){},所以就要把在AO对象中的b的属性改为function(){}
所以在第七句b的输出就是function(){},第八句直接被提升了,所以不用读了。
结果为:
function a(){}
123
123
function (){}
全局预编译:
生成一个GO global object对象(等价于window)
例:
<script> global = 100; function fn(){ console.log(global); global=200; console.log(global); var global=300; } fn(); var global; </script>
结果为:
undefined
200
执行过程:首先生成GO{
global:undefined
}
执行第一行:global = 100
执行fn()时生成AO{
global = undefined;
}
注意
js不是全文编译完成再执行,而是块编译,即一个script块中预编译然后执行,再按顺序预编译下一个script块再执行
但是此时上一个script快中的数据都是可用的了,而下一个块中的函数和变量则是不可用的。