什么是变量晋升?
定义:变量晋升是 当栈内存作用域造成时 ,JS 代码执行前,浏览器会将带有var, function 关键字 的变量提前进行申明 declare(值默认就是 undefined),定义 defined(就是赋值操作),这种事后解决的机制就叫做 变量晋升机制 也叫 预约义。
1. 带 Var 申明与不带 Var 申明的变量晋升
区别:var 操作符定义的变量会成为蕴含他的函数的局部变量。不过在函数外部定义变量时省略 var 操作符能够定义一个全局变量。
function foo() {
a = 0;
var b = 0;
}
foo()
console.log(a);//0
console.log(b);//Uncaught ReferenceError: b is not defined
var a = b =12 // 这里的 b 也是不带 var 的。/* 相当于
var a = 12;
b = 12
*/
var 申明相干 变量晋升练习题 –1:
console.log(a, b)
var a =12, b ='小白'
function foo(){console.log(a, b)
var a = b =13
console.log(a, b)
}
foo()
console.log(a, b)
/* 输入:undefined undefined
undefined "小白"
13 13
12 13
*/
解释 –1:
1)a 和 b 相当于定义了两个 window 变量,变量在代码执行前定义但未赋值,所以此时为 undefined undefined;
2)当代码执行到函数时,第一个 console.log(a, b)时,函数中的局部变量 a 的定义被晋升然而 b 未声明,因而 作用域链以后流动对象(函数上下文)找不到 b 的申明,从而寻找下一个 蕴含上下文 的流动对象 ,找到 b =’ 小白 ’,所以此时打印 undefined “ 小白 ”;
3)当执行了 var a = b =13,别离为函数局部变量 a 和全局变量 b 赋值后,此时打印的为 13 13;
4)因为函数赋值的变量 a 为局部变量,b 为全局变量,所以此时打印的 a,b 都是全局变量,因而 a 的值不变,b 的值赋值为 13,因而打印 12 13;
var 申明相干 变量晋升练习题 –2:
console.log(a, b)
var a =12, b = '小白'
function foo(){console.log(a, b)
}
foo()
console.log(a, b)
/* 输入:undefined undefined
12 "小白"
12 "小白"
*/
解释 –2:
1)var 变量申明晋升到顶端,但未赋值,因而打印为 undefined undefined
2)拜访全局变量
3)拜访全局变量
var 申明相干 变量晋升练习题 –3:
a = 2
function foo() {
var a = 12;
b = '小白'
console.log('b' in window)
console.log(a, b)
}
foo()
console.log(b)
console.log(a)
/* 输入:true
12 '小白'
小白
2
*/
解释 –3:
1)b 未用 var 操作符申明,因而默认定义未 window 变量,因而打印 true
2)函数执行过程中以后流动对象会被压入作用域链最顶端,从作用域链中以后流动对象中寻找变量,如果找不到,才回去寻找下一个蕴含上下文的流动对象,因而此时打印的 a 为 12;
3)在函数内部打印变量 a,b 那么以后的上下文的流动对象便是 window 对象, 因而打印的时全局变量 a,b;
var 申明相干 变量晋升练习题 –4:
fn();
console.log(v1);
console.log(v2);
console.log(v3);
function fn() {
var v1 = v2 = v3 = 2019;
console.log(v1);
console.log(v2);
console.log(v3);
}
/* 输入
2019
2019
2019
Uncaught ReferenceError: v1 is not defined
*/
解释 –4:
1)函数申明也会晋升,因而 2019、2019、2019 都是函数中打印的局部变量,当函数执行结束后,window 流动对象中并未定义 v1、v2、v3 因而此时报错。
2. 等号右边下的变量晋升
= 意味着赋值,因而右边应该为变量,左边都应该是值,因而只对等号右边的进行变量晋升
等号右边下的变量晋升相干 变量晋升练习题 –1:
print()
function print(){console.log('小白')
}
print()
/* 输入:小白
小白
*/
解释 –1:
1)因为 function 关键字也会变量晋升,因而不难理解相当于调用了两次 print()办法,因而打印两次小白;
等号右边下的变量晋升相干 变量晋升练习题 –2:
console.log(printf)//undefined
printf()//Error
var printf = function () {console.log('林一一')
}
printf()// 未执行
/* 输入
undefined
Uncaught TypeError: print is not a function
*/
解释 –2:
1)同样因为变量晋升机制带 var 的 print 是一开始值是 undefined
2)因为 1),并且此时并未给 printf 赋值成函数,所以 print() 这时还不是一个函数,所以报出 类型谬误 TypeError
等号右边下的变量晋升相干 变量晋升练习题 –3:
var fn = function sum() {console.log(sum);//=> 函数自身
console.log(1);//=>1
}
// sum();//=>Uncaught ReferenceError: sum is not defined
fn();
/* 输入:ƒ sum() {console.log(sum);//=> 函数自身
console.log(1);//=>1
}
1
*/
解释 –3:
1)sum()是匿名函数具体化后的名字,只能够在函数上下文中应用,因而蕴含上下文中拜访报 Error;
2)fn 定义晋升并初始化为 undefined 后,执行到 sum()为 fn 从新赋值,在调用 sum 能够失常输入。
2. 条件判断下的变量晋升
在 以后作用域 下不论条件是否成立 都要 进行变量晋升 ,function 在老版本浏览器 (IE10 以下) 的渲染机制下,申明和定义都解决 ,然而为了投合 es6 中的块作用域,新版本浏览器对于 在条件判断中的 function 不论条件是否成立,都只是先申明,没有定义 相似于 var,同时判断体中如果呈现了 let、const、function 会创立一个新的执行上下文即块级上下文。
条件判断下的变量晋升相干 变量晋升练习题(变量和函数晋升的区别)–1:
console.log(a); // undefined
console.log(b); // undefined
if (true) {console.log(a); // undefined
var a = 1;
console.log(b); // ƒ b() {console.log("1");}
function b() {console.log("1");
}
}
console.log(a); // 1
console.log(b()); // 1,undefined
/* 输入:undefined
undefined
ƒ b() {console.log("1");
}
1
1
undefined// 因为函数 b()没有返回值,因而 console.log(b()),b()执行时打印 1,执行结束打印 undefined
*/
解释 –1:
1)在以后作用域下不论条件是否成立都要进行变量晋升,为了投合 es6 中的块作用域,新版本浏览器对于在条件判断中的 function 不论条件是否成立,都只是先申明,没有值。因而,a、b 打印的都是 undifined。
2)在执行到 if(){}语句块中,变量执行到赋值语句才赋值,而函数间接赋值 。
3)if 执行完结,赋值操作完结,因而能够打印 a,b();
条件判断下的变量晋升相干 变量晋升练习题(变量晋升)–2:
console.log(a)
if(true){console.log(a)
var a = '小白'
}
console.log(a)
/* 输入
undefined
undefined
小白
/
解释 –2:
1)无论条件是否成立,都会进行变量晋升,因而打印 undefined;
2)赋值后从新打印 a;
条件判断下的变量晋升相干 变量晋升练习题 –3:
// console.log(f)//Uncaught ReferenceError: f is not defined
if(function f(){}){console.log(typeof f)//undefined
}
解释 –3:
1)if 中 () 内的表达式不会变量晋升;
2)判断的条件没有晋升,所以条件外部的 f 是未定义
认真比照如下代码:
console.log(globalPrint)//ƒ globalPrint(){}
console.log(fatherPrint)//undefined
console.log(childPrint)//undefined
function globalPrint(){}
if(true) {console.log(globalPrint)//ƒ globalPrint(){}
console.log(fatherPrint)//ƒ fatherPrint() {}
console.log(childPrint)//undefined
function fatherPrint() {}
if(false){console.log(globalPrint)//
console.log(fatherPrint)//
console.log(childPrint)//
function childPrint(){console.log(1)//
return "childPrint()的返回值";}
}
}
console.log(childPrint)//undefined
console.log(globalPrint)//ƒ globalPrint(){}
console.log(fatherPrint)//undefined
console.log(childPrint)//undefined
function globalPrint(){}
if(true) {console.log(globalPrint)//ƒ globalPrint(){}
console.log(fatherPrint)//ƒ fatherPrint() {}
console.log(childPrint)//undefined
function fatherPrint() {}
if(true){console.log(globalPrint)//ƒ globalPrint(){}
console.log(fatherPrint)//ƒ fatherPrint() {}
console.log(childPrint)//ƒ childPrint(){console.log(1)return "childPrint()的返回值";}
function childPrint(){console.log(1)//1
return "childPrint()的返回值";}
}
}
console.log(childPrint())//childPrint()的返回值
总结:
- if()判断语句中申明的办法无论在全局上下文或函数的执行上下文中,函数都无奈晋升。
- if{}语句块中,var 变量和函数都会作用域晋升,然而函数赋值是在进入语句块之后,而变量赋值是在执行赋值语句后。
- 函数变量会晋升,然而只有代码执行到函数申明的语句块时才会立刻赋值,因而父级上下文拜访子上下文中的函数时为 undefined(缺省值:申明了但未赋值)。
(未完 …)
参考资料:
- 彻底解决 JS 变量晋升 | 一题一图,超具体包教包会😉
- JS 中的变量晋升
了解尚浅,望不吝赐教!