预编译:作用域创建阶段就是预编译阶段。
预编译什么时候发生
要想完成预编译习题,先要知道预编译的时候做了什么事情:
预编译分为全局预编译和局部预编译,全局预编译发生在页面加载完成时执行,而局部预编译发生在函数执行的前一刻。
tip:预编译阶段发生变量声明和函数声明,没有初始化行为(赋值),匿名函数不参与预编译 。只有在解释执行阶段才会进行变量初始化 。
首先JavaScript的执行过程会先扫描一下整体语法语句,如果存在逻辑错误或者语法错误,那么直接报错,程序停止执行,没有错误的话,开始从上到下解释一行执行一行。
局部预编译
1.创建了AO对象
2.找形参和变量的声明作为AO对象的属性名值为undefined
3.实参和形参相统一
4.找函数声明 如果函数声明和变量声明名称一致的话会覆盖变量的声明
全局预编译的3个步骤:
1.创建了GO对象
2.找变量声明,将变量名作为GO属性名,值为undefined
3.查找函数声明,作为GO属性,值赋予函数体
由于全局中没有参数的的概念,所以省去了实参形参相统一这一步。
tip:GO对象是全局预编译,所以它优先于AO对象所创建和执行
练习
AO对象练习
function fn(a,c){
console.log(a)
var a = 123
console.log(a);
console.log(c);
function a(){}
if(false){
var d = 678
}
console.log(d);
console.log(b);
var b = function (){}
console.log(b);
function c(){}
console.log(c);
}
fn(1,2)
/*
执行分析
1.创建AO对象
AO:{
}
2.找形参和变量的声明作为AO对象的属性名值为undefined
AO:{
a:undefined,
c:undefined,
d:undefined,
b:undefined
}
3.实参和形参相统一
AO:{
a:1,
c:2,
d:undefined,
b:undefined
}
4.找函数声明 如果函数声明和变量声明名称一致的话会覆盖变量的声明
AO:{
a:function a(){},
c:function c(){},
d:undefined,
b:undefined
}
输出结果
ƒ a(){}
123
ƒ c(){}
undefined
undefined
ƒ (){}
ƒ c(){}
*/
AO,GO 一起练习
global = 100
function fn(){
console.log(global);
global = 200;
console.log(global);
var global = 300
}
fn()
var global;
/*
执行分析
先生成GO{}
GO{
global:100
fn:function(){}
}
函数fn执行前生成AO{}
AO{
global:undefined
}
AO中有自己的变量global,不调用GO的global。(就近原则)
输出结果
undefined
200
*/
function test(){
console.log(b)
if(a){
var b = 100;
}
c = 234;
console.log(c);
}
var a;
test();
a = 10;
console.log(c);
/*
执行分析
GO:{
a:undefined,
test:function test(){}
}
执行test()行时生成AO{} 不要受 if 的影响。
AO:{
b:undefined
}
*/
function test(test) {
console.log(test);
var test = 234;
console.log(test);
function test() { }
}
test(1);
var test = 123;
/*执行解析
GO:{
test:undefined,
test: function test(test) {
console.log(test);
var test = 234;
console.log(test);
function test() { }
}
}
AO:{
test:undefined=>1=> 234 =>function test() { }
}
*/
tip:关于GO对象和AO对象,它们俩是一个种链式关系,就拿上面的这个例子来说吧,如果在函数体的内部没有定义global变量,这也意味着AO对象中将有这个global这个属性。那如果没有会怎么办?它会去GO对象中寻找,说白了也就是一种就近原则。