前置:
首先我们得理解js运行顺序:
- 语法分析
- 预编译
- 解释执行
而在预编译过程中,会有两种提升:
- 函数声明整体提升:就是把任意位置的函数的声明都提到逻辑最前面
- 变量声明提升:把变量的声明也提到最前面。
那么什么叫函数声明和变量声明呢?
例如:
function a(){
};//这叫做函数声明
var b = function b(){
};//这个不是函数声明
var a =123;//这其实是两句话,其中var a;就是变量声明,而后面a=123;叫做变量赋值
我们再来看什么叫提升:
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);
提升之后:
function fn(a){
var a;var b;
function a(){
}
function d(){
}
console.log(a);
var a =123;
console.log(a);
console.log(a);
var b=function(){
}
console.log(b);
}
全局和局部
下面我们再来理解两个概念:
- 全局变量
- 局部变量
全局变量(GO):
- 作用域是全局
- 任何变量,假如未经声明就赋值,那么它就是全局变量
- 一切作用域在全局时的声明变量,也是全局变量
- 一切声明的全局变量都是window的属性
局部变量
- 写在函数体内的已声明变量
示例:
var a =123;
b=123;
console.log(window.a); //123
console.log(window.b); //123
function test(){
var a=c=1234;
console.log(a); //1234
console.log(c); //1234
}
test();
解释:a是声明的全局变量,就是window.a,因此第一个是123,第二个b是未经声明的全局变量,就是window.b,因此第二个是123
接下来来看test函数里面的执行顺序:
赋值顺序都是自右向左,因此我们将其拆分成:
c=1234;
a=c;
var a;
而c属于未声明变量,我们将其直接提升一个段位,即全局变量中,即window.c,再将1234赋值给c,c再赋值给a
函数预编译四步走:
-
创建AO对象Activation Object(执行期上下文),作用是理解的作用域
-
找形参和变量声明,将变量和形参名作为AO属性名,值为undefined,相当于
AO{
a:undefined;
b:undefined;
} -
将实参和形参相统一(把实参传到形参里)
-
在函数里面找函数声明,值赋予函数体
(这里注意,假如自己AO里有,那么就不用去找GO)
例如:
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中有a,b,c:undefined
// 第三步,a:1, b,c还是undefined
// 第四步, a: 1 b:function b(){} c:undefined d:function d(){}
然后开始执行,此时执行的时候代码内应该是这样(注释掉的已经预编译过):
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);
全局预编译三步走:
- 生成一个GO的对象,Global Object(window就是GO)
- 找形参和变量声明,值为undefined
- 在函数体里找函数声明,值赋给函数体
例如:
console.log(a);
var a=123;
function a(){
}
预编译:先声明a变量,值为undefined,然后将函数声明赋值给a,到此预编译结束,开始执行,因此console.log显示的是function a(){}
那么问题又来了,是先生成GO还是先生成AO?
想执行全局,当然是先有GO,然后才能有AO,
但是在函数体内预编译时,AO若有该变量,则用AO的,AO没有才用GO
例如:
console.log(test);
function test(){
console.log(test);
var test=234;
console.log(test);
function test(){
}
}
test(1);
var test=123;
console.log(test);
先开始GO:
第一二步
GO{
test=undefined;
}
第三步
GO{
test= function test(){};
}
至此全局预编译完成,开始执行
因此 第一个控制台打印为
test =function test(){};
而执行test(1)之前先生成AO
AO开始:
AO{
test:undefined
}
AO{
test= function test(){}
}
此时函数内部预编译完成后等价于:
function test(){
console.log(test);
test=234;
console.log(test);
//function test(){}
}
然后函数内部开始运行,第二个控制台打印 test= function test(){},然后将234赋值给test,第三个打印234
欢迎大家点赞收藏加关注!!!有不懂的可以随时留言,我们互相探讨