JavaScript 预编译:函数声明提升,变量声明提升

我们都知道 js 中函数声明会提升,变量声明会提升(赋值不提升)。那为什么会这样呢,这就涉及到 js 预编译。

js 运行三部曲

  1. 语法解析——检查有无语法错误;
  2. 预编译;
  3. 解释运行——将 js 翻译成计算机识别的语言(0 和 1组成的),翻译一行执行一行。

预编译什么时候发生

  1. 预编译不仅发生在函数体内,还发生在全局;
  2. 预编译发生在函数或代码执行前一刻。

说预编译之前有两点需要我们记住

  1. 暗示全局变量(imply golbal):任何变量未经声明(没有用 var 关键字声明)就赋值,此变量就为全局变量所有;

     a = 123;                      // a 是全局变量
     var a = b = 123;              // b 是全局变量。赋值一定是从右向左的
    
  2. 一切声明的全局变量都是 window 的属性(window 就是全局)。

预编译的步骤

函数:
  1. 创建 AO( Activation Object ) 对象(执行期上下文);
  2. 找形参和变量声明,将形参和变量名作为 AO 对象的属性名,值为 undefined(有重复的名称只写一个即可);
  3. 将形参与实参值统一(用实参的值替换 undefined);
  4. 在函数体中找函数声明,将函数名添加到 AO 对象的属性中,值为函数体(如属性名重复,则覆盖前面的)。

最后得到一个 AO 对象,代码运行时按照 AO 对象来。例如:

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() {}
	console.log(d);
}

fn(1);

以上代码按照前面的四个步骤:

1、创建 AO 对象
AO = {}

2、找形参和变量声明,将形参和变量名作为 AO 对象的属性名,值为 undefined
AO = {
	a: undefined,
	b: undefined
}

3、将形参与实参值统一
AO = {
	a: 1,
	b: undefined
}

4、在函数体中找函数声明,将函数名添加到 AO 对象的属性中,值为函数体。
AO = {
	a: function a() {},
	b: undefined,
	d: function d() {}
}
预编译完成后得到的 AO 对象

预编译完成后运行代码:

function fn(a) {
	console.log(a);          // function a() {}
	var a = 123;
	console.log(a);          // 123 这时的 AO 对象变成 
								AO = {
									a: 123,
									b: undefined,
									d: function d() {}
								}
	function a() {}
	console.log(a);          // 123
	var b = function() {}    // 这时的 AO 对象变成
								AO = {
									a: 123,
									b: function() {},
									d: function d() {}
								}
	console.log(b);          // function() {}    
	function d() {}
	console.log(d);          // function d() {}
}

fn(1);
全局:
  1. 创建 GO( Global Object ) 对象;
  2. 找变量声明;
  3. 找函数声明。

全局变量也是一样的:

console.log(a);
var a = 123;
function a() {}
console.log(a);

第一步:创建 GO( Global Object ) 对象
GO = {}

第二步:找变量声明
GO = {
	a: undefined,
}

第三步:找函数声明
GO = {
	a: function a() {},
}

预编译完成后得到的 GO 对象

函数运行结果:
console.log(a);          // function a() {}
var a = 123;
function a() {}
console.log(a);          // 123

再看几个例子

console.log(test);
function test(test) {
	console.log(test);
	var test = 321;
	console.log(test);
	function test() {}
}
test(1);
var test = 123;
console.log(test);

按照以上的步骤得到的两个对象分别是:

GO = {
	 test: function test(test) {
		console.log(test);
		var test = 321;
		console.log(test);
		function test() {}
	}
}

AO = {
	test: function test() {}
}

执行结果:

function test(test) {
	console.log(test);
	var test = 321;
	console.log(test);
	function test() {}
}

function test() {}
321
123

global = 100;
function fn4() {
	console.log(global);
	global = 200;
	console.log(global);
	var global = 300;
}
fn4();
var global;

两个对象:

GO = {
	global: 100
}

AO = {
	global: undefined
}

执行结果:

undefined
200

function fn5() {
	console.log(b);
	if(a) {
		var b = 100;
	}
	c = 234;
	console.log(c);
}
var a;
fn5();
a = 10;
console.log(c);

两个对象:

GO = {
	a: undefined,
	fn5: function fn5() {
	    console.log(b);
	    if(a) {
	        var b = 100;
	    }
	    c = 234;
	    console.log(c);
	},
	c: undefined          // 变量 c 未经声明(没有用 var 关键字声明)就赋值,此变量就为全局变量所有,所以添加到 GO 对象中
}

AO = {
	b: undefined
}
对于函数体内变量的值(如此例中的 c),AO对象中有就用AO对象中的值,AO对象中没有就用GO对象中的值

运行结果:

undefined
234
234

总结

综上,所以有我们都知道的:

  • 函数声明提升
  • 变量声明提升(赋值不提升)
  • 10
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值