JavaScript初学 学习笔记4:JavaScript之预编译深入浅出(看完肯定就懂了,别再放收到藏夹吃灰了)

前言

一定要看完,千万别放收藏夹里吃灰
话不多说
先来看如下一段代码

console.log(a);
var a = 10;

在这里插入图片描述
我们都知道,JavaScript是解释性语言,解释一行执行一行,可是 a 变量的申明在后面一句,为什么输出不报错呢?
我们应该听说过JavaScript运行三部曲:

  1. 分析代码
  2. 预编译
  3. 执行语句

而这里的这个原因,就是和预编译相关了
既然如此,到底什么是预编译

预编译又称为预处理,是做些代码文本的替换工作。是整个编译过程的最先做的工作。 -------------百度百科

可能这么看有点难懂,没事,我们慢慢看下去

预编译前奏(铺垫)

  1. imply global 暗示全局变量:即任何变量,如果变量未经申明就赋值,此变量就为全局对象所有
  2. 一切申明的全局变量全是window的属性

我直接用代码来展示吧

//console.log(a);   单独这么一条语句肯定报错
a = 10;
console.log(a);

在这里插入图片描述
下面两句并未报错
同时控制台上输入window.a,输出的是10
在这里插入图片描述
我们说了,任何变量,如果变量未经申明就赋值,此变量就为全局对象所有,至于这个window就是全局对象

//上述代码可以这么理解
window{
	a : 10;
}
//未申明的变量a赋值,则相当于为window对象增加了一个叫a的属性,属性值为10

一切申明的全局变量全是window的属性
这条就简单了,在全局范围内申明的变量,都是window的属性

var a = 111;
-->window.a = 111;
console.log(a) ==> console.log(window.a)

再来看一段代码

function fn() {
	var a = b = 10;
}
fn();
console.log(window.b);

在这里插入图片描述
赋值的顺序是从右往左,所以顺序是:

  1. b = 10
  2. var a = b

那么对于b来说,b是一个未声明的变量,对他进行了赋值,那么它就属于全局对象window里的属性了
那么这一点就全都搞懂了

预编译详解

关于预编译,我们一定听过很多人说的

  1. 函数声明整体提升
  2. 变量 声明提升

这两句话,很好理解,虽然能解决大多数问题,但是,并不是我所愿的,就像下面这个问题,他并不能解决。


先来看一段代码
试着说出其输出结果

function f(a) {
	console.log(a);
	var a = 10;
	console.log(a);
	function a() {}
	console.log(a);
	var b = function () {}
	console.log(b);
}
f(1);

我们通过这段代码来讲解预编译的相关知识

要解决这个问题,我们肯定要清楚,谁先提升到谁前面,谁又要覆盖谁
下面就是预编译的重点:

  1. 创建AO对象
  2. 找形参和变量声明,将变量和形参名作为AO属性名,值为undefined
  3. 将实参值和形参统一
  4. 在函数体里面找函数声明,值赋予函数体

我们按照步骤来


1

创建AO对象(Activation Object 执行期上下文)

AO{
}

2

找形参和变量声明,将变量和形参名作为AO属性名,值为undefined

AO{
	a : undefined,  //a先是传入了形参,之后因为有一个变量 var a 所以现在传入的是 var a
	b : undefined;  //函数表达式也是申明的
	//将其均赋值 undefined
}

3

将实参值和形参统一

AO{
	a : 1,          //a先是传入了形参,之后因为有一个变量 var a 所以现在传入的是 var a
	b : undefined;  //函数表达式也是申明的
	//将其均赋值 undefined
}

4

在函数体里面找函数声明,值赋予函数体

AO{
	a : function a () {}, //将函数体赋值给属性
	b : undefined;	  	  //b是函数表达式,不是函数申明
}

预编译发生在执行函数之前

function f(a) {
	console.log(a);
	var a = 10;
	console.log(a);
	function a() {}
	console.log(a);
	var b = function () {}
	console.log(b);
}
f(1);

第一句就是console.log(a)这时候我们上哪找这个a呢?
这个时候是去AO对象里面找的,所以输出的a是 函数a

第二、三句 var a = 10这一步其实是不完全执行,因为预编译的第二步已经找找到变量声明(变量 声明提升),将变量和形参名作为AO属性名,所以这一句执行的其实是 a = 10
这个时候,将AO对象的a的值修改为10,所以输出的是10

第四、五句 function a() {} 这句话在预编译第四步的时候提升上去了(函数声明整体提升) 所以就不看了,此时AO对象里面的a任然是 10 所以输出也是10

第六、七句虽然第六句和第二句类似,AO对象里b的值修改成是 函数b
所以也将输出函数b


最终输出如下
在这里插入图片描述
再来看一段代码练练手吧

function f(a,b) {
	console.log(a);
	console.log(b);
	var b = 234;
	console.log(b);
	a = 123;
	console.log(a);
	function a(){}
	var a ;
	b = 345;
	var b = function () {}
	console.log(a);
	console.log(b);
}
f(1);

结果
在这里插入图片描述
注意当函数退出的时候AO对象会被销毁

全局发生的预编译

预编译不止发生在函数体,还发生在全局

console.log(a);
var a = 1;
//输出undefined
console.log(a);
var a = 1;
function a() {}
//输出函数 a

全局的预编译和函数的预编译有点差别,它没有第三步,也就是说,他没有形参
同时它的第一步不是生成AO对象,是生成 GO对象(Global Object),其他的步骤都一样

看完应该就懂了吧,别再放收藏夹吃灰去了

END

猜你喜欢

转载自blog.csdn.net/leowahaha/article/details/107544502
今日推荐