JavaScript预编译(执行期上下文)总结
预编译(又叫“执行期上下文”)
一般来说,预编译与闭包、作用域链和闭包是密切相关的,好多人包括在企业开发的人都听说过一句话叫做“函数声明整体提升”。这个就是预编译后的一个小结果,所以今天自己总结了一下。分为以下几个步骤来说。
大家先看看这段代码
console.log(a);
var a = 1;
结果:
为什么变量的声明在输出的下面,却没有报错?值为什么是undefined?
大家再看看下面这段代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
function test(a,b){
console.log(a);
var a = 'demo';
console.log(a);
function a(){}
console.log(a);
var a = function (){}
console.log(a);
console.log(b)
var b = 1;
}
test(1);
</script>
</body>
</html>
大家看到这段代码是不是很震惊呢?形参、变量名、函数名都是“a”,那么输出的结果会是怎样呢?
和大家想的一样吗?大家心里是不是有这么几个问题,前四个输出的都是a,为什么输出的结果不一样呢?而且b变量不是把1赋值给它了吗,为什么会是undefined?接下来咱们来讲讲什么是预编译(执行期上下文)。到最后大家就明白了。
预编译(‘执行期上下文’)
预编译发生在函数执行之前。划重点了啊!!!!! 这句话很重要,函数执行之前也就是在这段程序开始之前,浏览器对马上要执行的函数进行预编译!!
预编译四部曲
- 创建AO对象
- 找形参和变量声明,将变量和形参作为AO属性名,值为undefined
- 将实参和形参相统一
- 在函数体里找到函数声明,值赋予函数体
最后程序输出变量值的时候,就是从AO对象中拿。
以第一个问题为例咱们开始讲解。以下过程都是在系统内部完成的
1、创建AO对象
var AO = {
}
2、找形参和变量声明,将变量和形参作为AO属性名,值为undefined
这里的形参首先出现的是a,b。变量为a,b。
大家同时也要区分开函数声明和表达式。function demo(){}这个叫做函数声明,var demo = function (){}是表达式。它是由两部分组成,var demo 变量声明。demo = function(){}变量赋值。 同理 var demo = ‘aaa’ 也是一样的。这里说的是找变量。
var AO = {
a = undefined;
b = undefined;
}
3、将实参和形参相统一
这里的实参只有一个是1。
var AO = {
a =1;
b = undefined;
}
4、在函数体里找到函数声明,值赋予函数体
这里只有第十三行是函数声明,第十五行不是。上面已经讲过了。
var AO = {
a = function a(){};
b = undefined;
}
到此为止预编译结束。然后调用函数,系统执行程序。
5、系统执行程序(解释一行执行一行)
- console.log(a),输出a。这是输出的值为function a(){};
- var a = ‘demo’ 其实就是a = ‘demo’。因为var a已经在预编译中ok了。AO中的a的值变为’demo’;
- console.log(a) ,这时候输出的就是 demo;
- function a(){} 在预编译中已经用过了就不用了。
- console.log(a) , 输出 demo ;
- var a = function (){} AO中的a的值变为function (){};
- console.log(a),输出 function (){};
- console.log(b) b中的值一直未undefined ;输出 undefined;
- var b = 1; 将AO对象中的b的值变为1;
这样就解释了刚开始提出的两个问题。这种作用在全局中也是可以的,预编译的作用域为全局的变量时,创建的AO对象变为GO对象,相当于省略了第三步。其他的都一样。
如果你喜欢记得转载和点赞哦,欢迎留言评论!!!
接下来我会为大家分享作用域、作用域链和闭包的知识。