2019-11-18 seo达人
JS----预编译及变量提升详解
JS属于解释型语言,在执行过程中顺序执行,但是会分块先预编译然后才执行。因此在JS中存在一种变量提升的现象。搞懂预编译环节,变量提升自然而然也就懂了。本文讲围绕以下几点进行介绍(变量提升会穿插在其中讲解):
预编译执行步骤
示例演示
预编译执行步骤
预编译发生在函数执行的前一刻,过程如下:
创建AO对象,执行期上下文(后面更新关于执行期上下文详解)。
寻找函数的形参和变量声明,将变量和形参名作为AO对象的属性名,值设定为undefined.
将形参和实参相统一,即更改形参后的undefined为具体的形参值。
寻找函数中的函数声明,将函数名作为AO属性名,值为函数体。
至此,预编译环节结束,函数中咯变量按照最终AO对象中的值开始执行。接下来,结合示例演示就会更加清晰。
作者:北海北方
链接:https://juejin.im/post/5aa6693df265da23884cb571
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
示例演示
我们先来看下面这段代码:
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(){};
}
//调用函数
fn(1);
作者:北海北方
链接:https://juejin.im/post/5aa6693df265da23884cb571
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
接下来我们来按照前面的步骤详细分析它的预编译执行过程:
创建AO对象
AO{
//空对象
}
复制代码
找形参和变量声明
AO{
a : undefined,
b : undefined
}
复制代码
形参和实参相统一
AO{
a : 1,
b : undefined
}
复制代码
找函数声明
AO{
a : function a(){},
b : undefined,
d : function d(){}
}
复制代码预编译环节就此结束,此时的AO对象已经更新为:
AO{
a : function a(){},
b : undefined,
d : function d(){}
}
复制代码函数开始逐行顺序执行:
function fn(a){
console.log(a);// 输出functiona(){}
var a = 123;//执行到这里重新对a赋,AO对象再一次更新
console.log(a);// 输出123
function a(){};//预编译环节已经进行了变量提升,故执行时不在看这行代码
console.log(a);// 输出123
var b = function(){};//这个是函数表达式不是函数声明,故不能提升,会对AO中的b重新赋值
console.log(b);//输出function(){}
function d(){};
}
复制代码至此,函数执行完毕,销毁AO对象。
我们再来看几个例子,熟悉函数的预编译过程。
示例一:
function test (a,b){
console.log(a);
c = 0;
var c;
a = 3;
b = 2;
console.log(b);
function b(){};
function d(){};
console.log(b);
}
//调用函数
test(1);
复制代码它的AO创建过程如下(此处省略创建空AO对象的部分,下文同):
AO1{
a : undefined,
b : undefined,
c : undefined
}
AO2{
a : 1,
b : undefined,
c : undefined
}
AO3{
a : 1,
b : function b(){},
c : undefined,
d : function d(){}
}
复制代码至此预编译环节完成,开始执行:
function test (a,b){
console.log(a); //输出1
c = 0; //给AO对象中的c重新赋值0
var c;//预编译环节变量提升,不再读此行代码
a = 3;//给AO对象中的a重新赋值3
b = 2;//给AO对象中的b重新赋值2
console.log(b);//输出2
function b(){};//预编译环节变量提升,执行时不再读这行代码
function d(){};//预编译环节变量提升,执行时不再读这行代码
console.log(b);//输出2
}
//调用函数
test(1);
复制代码示例二:
这个例子中我们引入全局对象GO。GO与AO的过程类似
function test(){
var a = b = 123;
}
test();
复制代码此函数的执行过程:先把123赋给b,再声明a,再把b赋给a。此时变量b未经声明就赋值,为全局变量。预编译环节如下:
GO1{
b : undefined
}
AO1{
a : undefined
}
GO2{
b : 123;
}
AO2{
a : 123;
}
复制代码示例三 :
console.log(test);
function test(test){
console.log(test);
var test = 234;
console.log(test);
function test(){};
}
test(1);
var test = 123;
复制代码我们来看它的预编译过程:
//执行前(页面加载完成时)生成GO对象
GO1{
test : undefined
}
GO2{
test : function(){}
}
//输出 function test(){...}
//执行test()前生成它的AO对象
AO1{
test : undefined
}
AO2{
test : 1
}
AO3{
test : function test(){}
}
//预编译结束开始执行test(1);
AO4{
test : 234
}
//输出234
复制代码示例四:
function demo(){
console.log(b);
if(a){
var b = 100;
}
console.log(b);
c = 234;
console.log(c);
}
var a;
demo();
a = 10;
console.log(c);
复制代码我们来看它的预编译过程:
//首先是全局对象GO
GO1{
a : undefined
}
G02{
a : undefined,
demo : function demo(){}
}
//执行demo()前预编译,由于demo中的c未声明就使用故为全局对象
//输出undefined
GO3{
a : undefined,
demo : function demo(){}
c : undefined
}
//此时a还是undefined,故不执行if()代码块
//输出还是undefined
GO4{
a : undefined,
demo : function demo(){}
c : 234;
}
//输出234
GO5{
a : 10,
demo : function demo(){}
c : 234;
}
//输出234