JavaScript高级程序设计----深入理解、总结变量提升
什么是变量提升?
定义:变量提升是当栈内存作用域形成时,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 definedvar 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
*/
【JavaScript高级程序设计----深入理解、总结变量提升】解释--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 = 2function 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中的变量提升
理解尚浅,望不吝赐教!
推荐阅读
- JAVA高级|Git 工具 - 高级合并
- Git 技巧之高级配置用户信息
- JavaScript|JavaScript-函数方法apply()、call()、bind()
- JavaScript|JavaScript(第三章第1课)(内部对象--(Date))
- Everything 的高级用法
- JavaScript实现简易飞机大战
- JavaScript实现微信飞机大战游戏
- javascript实现简单飞机大战小游戏
- 全网稀缺Vue2.0高级实战独立开发专属音乐WebAPP
- 什么是结构化程序设计(图文详解)