什么是变量提升?
变量提升-Hoisting是JavaScript中的一种行为,指的是变量和函数的声明在编译阶段被“提升”到它们所在作用域的顶部,即使它们在代码中实际出现的位置是在后面。变量提升适用于使用 var 声明的变量和所有的函数声明,而不包括 let 和 const 声明的变量。赋值部分则不会被提升,赋值操作依然发生在代码执行的地方。
- 函数声明:函数的整个声明和定义都会被提升。
var声明:只提升声明,赋值保持在原来的位置。let和const:不会被提升(即使在它们的作用域中,它们也会存在“暂时性死区”(Temporal Dead Zone, TDZ),只有在它们的声明语句之后才能被访问)。
1.带var 以及 不带var 的区别
console.log(a, b) // undefined undefined
var a = 1, b = 10
function foo() {
// 2
console.log(a, b) // undefined 10
// 3
var a = b = 20
console.log(a, b) // 20 20
}
foo()
console.log(a, b) // 1 20
foo函数代码,等效于下面的
function foo() {
var a; // 变量提升,a 是局部变量,初始 undefined
console.log(a, b); // 2. undefined 10
a = b = 20; // 关键行!
console.log(a, b); // 3. 20 20
}
首先对上述代码进行分析
第2行代码,首先对a以及b进行了声明以及定义,此时a和b都是var进行声明的,且在全局作用域下
第7行代码,这里在函数内部对a以及b进行了声明以及定义,此时a是局部的var进行声明的,在函数局部进行变量提升,b是隐式全局变量
对于上面的两行代码有了了解之后,对于输出就应该没什么问题了
第1行,由于进行了变量提升,所以输出都是 undefined
第5行,在局部,变量a是局部变量,且需要进行变量提升,所以第5行a = undefined,b此时是全局变量的引用,所以b = 10
第8行,由于第7行进行了赋值,此时按照逻辑直接输出 20 20
第11行,此时a输出全局变量a = 1,,也就是第2行的定义,b此时已经被函数内部的隐式全局变量b = 20覆盖,此时b = 20
sample1
console.log(a, b) // undefined undefined
var a = 1, b = 10
function foo() {
console.log(a, b) // 1 10
}
foo()
console.log(a, b) // 1 10
由于在foo内部没有任何对于a以及b的声明,此时就向上一级进行查询a和b的值
sample2
function foo() {
console.log(a)
a = 1;
b = 10
console.log('b' in window)
console.log(a, b)
}
foo()
对于上述代码,直接报错
由于 JavaScript 的作用域链和变量提升规则,如果没有在函数内用 var、let 或 const 声明,a 将会被看作是一个全局变量。由于在全局内,未对a进行定义,所以报错
sample3
function foo() {
// console.log(a)
a = 1;
b = 10
console.log('b' in window) // true
console.log(a, b)
}
foo()
如果对上述代码的第二行注释,此时会将a以及b都隐式的声明为全局变量,所以此时第五行输出为 true
sample4
fn();
// console.log(v1);
console.log(v2);
console.log(v3);
function fn() {
var v1 = v2 = v3 = 1;
console.log(v1);
console.log(v2);
console.log(v3);
}
对于上述代码,输出为 5 个 1
如果不注释第二行,此时的输出呢?
此时,v1是在函数内局部用var定义,在全局作用域下是没有v1的,此时直接输出3个1之后然后进行报错
sample5
if (true) {
var blockVar = "var in block";
let blockLet = "let in block";
const blockConst = "const in block";
}
console.log(blockVar); // 输出: "var in block"(因为 var 提升到全局)
console.log(blockLet); // 报错:blockLet is not defined
console.log(blockConst); // 报错:blockConst is not defined
function fn() {
var funcVar = "var in function";
let funcLet = "let in function";
const funcConst = "const in function";
}
fn();
console.log(funcVar); // 报错:funcVar is not defined
console.log(funcLet); // 报错:funcLet is not defined
console.log(funcConst); // 报错:funcConst is not defined
作用域
上面的一个是块级作用域,所以存在变量提升,var的声明下可以正常输出,let/const就不能行。
另外一个就是函数作用域,这里就不存在变量提升,function会隔离所有的变量声明。
2.等号左边的变量提升
console.log(fn) // undefined
fn() // 报错
var fn = function () {
console.log(111)
}
fn()
由于是var进行定义的,此时进行变量提升,第一行fn输出为undefined,此时第二行就会报错,因为是undefined(),此时未对这个函数进行定义,所以报错
如果主事上述第二行代码,可以正常运行,在进行变量提升之后,然后在第三行进行赋值,此时可以正常运转