1. 什么是提升
简单点来说,就是JS在执行代码前会进行预编译,预编译期间会将变量声明与函数声明提升至其对应作用域的最顶端
console.log(a) // undefined
var a = 1;
console.log(func) // func() {console.log(123)}
function func() {
console.log(123)
}
以上实例中,用var声明的变量提升到了当前作用域的最顶端,所以这边输出会是undefined;函数声明也会提升到最顶端,所以这边可以输出函数相关方法定义
2. 面试真题
题目1:var定义变量提升
console.log(a)
var a = 1
// 输出结果:undefined
以上实例实际上是a变量提升了,实际预编译成如下代码:
var a;
console.log(a)
a = 1
所以这边a输出会是undefined
题目2:var和setTimeout混用题型
for (var i = 0; i <= 3; i++) {
setTimeout(function () { //同步注册回调函数到异步的宏任务队列
console.log(i) //执行此代码时,for循环已经执行完毕。
}, 0)
}
// 输出结果:4 4 4 4
讲解: 这里需要了解JS的运行机制,JS执行顺序是先同步后异步;所以这里setTimeout是属于异步任务,先执行完同步任务,然后才会执行setTimeout,当执行到setTimeout函数内部的时候,for循环已经执行完毕了,所以此时输出的会是4个4,当执行完i=3之后,会去执行setTimeout,但是此时for循环已经执行完了,所以这边后面传入setTimeout中的实际上是4
题目3:let和setTimeout混用题型
for (let i = 0; i <= 3; i++) {
setTimeout(function () {
console.log(i)
}, 0)
}
// 输出结果:0 1 2 3
讲解: 把题目2中的var改成let就能输出我们希望正常的值了,var会出现那种情况是因为变量会被提升,并且var不受当前作用域的约束;而let就不一样,let声明的变量在for循环体作用域中使用的时候,变量会被固定,不会被外界干扰。
题目4:同步和异步题型
for (var i = 0; i <= 3; i++) {
console.log(i) // 0 1 2 3
setTimeout(function () {
console.log(i) // 4 4 4 4
}, 0)
}
讲解: 这里第一个会输出0 1 2 3是因为是同步操作,所以会依次输出对应的i值;而setTimeout中会等上面同步操作执行完之后,才会去执行,所以这边输出的就是4个4,详细解析请看上面题目2解析
题目5:函数和变量提升优先级
function fun() {
var a = 99;
fun1();
console.log(a);
function fun1() {
console.log(a);
var a = 10;
console.log(a);
}
}
fun()
// 输出结果:undefined 10 99
讲解: 当局部变量有定义参数a,则遵循就近原则,现在执行fun1函数,此时fun1函数中,变量a会被提升到fun1函数顶部,所以此时输出fun1中实际预编译如下:
function fun1() {
var a;
console.log(a)
a = 10
console.log(a)
}
所以这边先输出undefined后面输出10;当fun1方法执行完毕,则继续往下执行,此时程序下一步输出的a就是属于fun中的变量a,故而输出99
题目6: 函数提升优先级比var高
var a = 1;
function func() {
console.log(a)
var a = 2
function a() {
console.log(a)
}
}
func()
// 输出结果:function a() {console.log(a)}
讲解: 当var变量声明和function声明同名时,函数声明优先,所以这边输出的是个函数方法
题目7:函数提升和变量提升
var a = 1;
function func() {
console.log(a)
var a = 2
function a() {
console.log(a)
}
console.log(a)
}
func()
// 输出结果:function a() {console.log(a)} 2
讲解: 这里因为函数声明比变量声明优先级更高,所以预编译的时候是先声明函数的,所以第一个输出的是一个函数方法,第二个输出一个赋值变量;以上实际预编译为:
var a = 1;
function func() {
function a() {
console.log(a)
}
console.log(a)
a = 2;
console.log(a)
}
这里会把函数声明提取到作用域顶部,所以此时第一个输出的函数方法,第二个输出是变量赋值
3. 解题思路总结
- 外部和内部都定义var变量,则就近原则提升
- 如果遇到函数声明和var声明同名,则函数声明优先级更高
- 遇到setTimemout异步方法和var混用,先执行同步的,后执行异步(setTimeout)