js预编译过程

预编译按执行节点可分为两个环节:
1、JavaScript代码执行之前
2、函数代码执行之前

->过程都是先筛选一遍变量(包括var/let/…/形参/实参/function/if语句中的),再执行一遍

->为了分析,筛选过程用一个全局对象GO(Global Obj)存放全局的变量状态,AO(Activetion Object,执行期对象)存放函数内的变量状态

-> 例子:

<script>
    console.log(a,b)
    a = 1;
    function b(c, d){
        function f(){};
        c = 9;
        var a = 8;
        console.log(a)
        e = 5;
        console.log(e)
    }
    b(3)
    var a = function(){};
    console.log(a)
    var b = 2;
    console.log(b)
    console.log(e)
</script>

-> 开始:
1、JavaScript代码执行之前
(1)筛选
①按顺序筛选var声明语句,变量存放到GO,状态为undefined,
未声明直接赋值的(如a=2)同样按undefined存放,重复声明的取代之前的;

GO:{
	a: undefined,
	b: undefined
}

②再走一遍筛选出function函数体语句(注意是非函数调用语句),如:function b(){},函数名存放到GO,状态为函数体本身。与①重复的取代,把①的变量的undefined赋值为函数体

GO:{
	a: undefined,
	b: function b(c,d){...}
}

(2)执行
注:执行语句在筛选过程先全部忽略不计
③筛选完从头开始执行,赋值、console之类的语句,函数体在这里忽略不计如function b(c, d){…}

console.log(a,b) // 打印 undefined  ƒ b(c, d){...}
a = 1;
/*
GO:{
	a: 1,
	b: function b(c,d){...}
}
*/
function b(c, d){  //忽略
    function f(){};
    c = 9;
    var a = 8;
    console.log(a)
    e = 5;
    console.log(e)
}
b(3)  //遇到函数调用,进入函数进行第二环节↓
var a = function(){};
console.log(a)
var b = 2;
console.log(b)
console.log(e)

执行到函数调用语句(如fn())进入第二个环节↓
这时的

GO:{
	a: 1,
	b: function b(c,d){...}
}

2、函数代码执行之前
函数内部和“JavaScript代码执行之前”类似
创建一个AO对象
(1)筛选
④将函数形参放入AO,状态为undefined:AO:{c:undefined,d:undefined}
⑤筛选内部的var声明语句,变量存放到AO,状态为undefined:AO:{c:undefined,d:undefined,a:undefined}
⑥筛选未声明直接赋值的语句(如e=5),进行变量提升属于全局变量,按全局变量保存到GO,状态为undefined;若函数中声明过,按undefined存放于AO
AO:{c:undefined,d:undefined,a:undefined}

GO:{
	a: 1,
	b: function b(c,d){...},
	e:undefined,
}

⑦实参赋给形参的状态AO:{c:3,d:undefined,a:undefined}
⑧筛选内部的函数体声明语句,函数名存放到AO,状态为函数体:

AO:{
	c:3,
	d:undefined,
	a:undefined,
	f:function f(){}
}

(2)执行
⑨ 与③的执行一样,另:当要执行的变量在函数中不存在时,向函数外找

function b(c, d){
    function f(){}; //忽略
    c = 9;
    /*
    AO:{
		c:9,
		d:undefined,
		a:undefined,
		f:function f(){}
	}
    */
    var a = 8;
    /*
    AO:{
		c:9,
		d:undefined,
		a:8,
		f:function f(){}
	}
    */
    console.log(a) //打印 8
    e = 5;
    /*
    GO:{
		a: 1,
		b: function b(c,d){...},
		e: 5,
	}*/
    console.log(e) //打印 5
}

函数调用执行完继续执行下面的语句

console.log(a,b) // 打印 undefined  ƒ b(c, d){...}
a = 1;
/*
GO:{
	a: 1,
	b: function b(c,d){...}
}
*/
function b(c, d){  //忽略
    function f(){};
    c = 9;
    var a = 8;
    console.log(a)
    e = 5;
    console.log(e)
}
b(3)  //遇到函数调用,进入函数进行第二环节↓
var a = function(){};
/*
GO:{
	a: function(){},
	b: function b(c,d){...}
}
*/
console.log(a) //打印 ƒ (){}
var b = 2;
/*
GO:{
	a: function(){},
	b: 2
}
*/
console.log(b) //打印 2
console.log(e) //打印 5

完整打印:

undefined ƒ b(c, d){
        function f(){};
        c = 9;
        var a = 8;
        console.log(a)
        e = 5;
        console.log(e)
    }
8
5
ƒ (){}
2
5

注:①连等赋值

let a = b = 2;
// 相当于
b = 2// 未申明自动提升为全局变量
let a = b;

②if语句中有定义也同步拿出来
③let与var的区别在于 let存在块级作用域不存在变量提升

为了完全理解预编译过程找了好几篇文章才得到较完整的信息,用这个思路能正确地分析得到编译结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值