没错,经典变量提升,经典炒冷饭,我将其称之为典中典。
什么是提升?
引擎会在解释 JavaScript 代码之前首先对其进行编译,编译过程中的一部分工作就是找到所有的声明,并用合适的作用域将他们关联起来,这也正是词法作用域的核心内容。
通俗点说就是变量提升就是将变量声明提升到它所在作用域的最顶端。
在 ES6 之前,JavaScript 是没有块级作用域这么一说的({}内即为一个块级作用域),只有全局作用域和函数作用域。
变量提升
全局作用域中声明的变量会提升至全局最顶层
函数内声明的变量只会提升至该函数作用域最顶层
例:
var a;
console.log(a); // undefined
a = "a";
var foo = () => {
var a; // 全局变量会被局部作用域中的同名变量覆盖
console.log(a); // undefined
a = "a1";
}
foo();
ES6 新增了 let 和 const 关键字,让我们在写 JavaScript 代码的时候更符合直觉性。
函数提升
函数提升和变量提升是一个道理,都是讲本来应该在后面执行的代码全部放到前面来。
不同之处在于 —— 变量提升是提升声明,函数提升只提升函数声明,不会提升函数表达式。
函数提升的优先级大于变量提升 —— 先提升函数,再提升变量
举个栗子:
console.log(foo1);
foo1();
console.log(foo2);
foo2();
function foo1 () {
console.log("foo1");
};
var foo2 = function () {
console.log("foo2");
};
经过预编译后它长这样:
var foo1 = function () {
console.log("foo1");
};
var foo2;
console.log(foo1); // Function foo1(){...}
foo1(); // foo1
console.log(foo2); // undefined
foo2(); // TypeError: foo2 is not a function
foo2 = function () {
console.log("foo2");
};
看完上面的例子,有点同学可能还有点迷糊,那我们再来个:
console.log(fun)
console.log(fun2)
var fun = 10
function fun(){
console.log("hello fun")
}
var fun2 = function(){
console.log("hello fun2")
}
console.log(fun)
console.log(fun2)
好,思考一下,参照上面的例子将它预编译一遍~
// 函数提升
function fun(){
console.log("hello fun")
}
// 变量提升
var fun;
var fun2;
console.log(fun) // Function fun(){...}
console.log(fun2) // undefined
fun = 10
fun2 = function(){
console.log("hello fun2")
}
console.log(fun) // 10
console.log(fun2) // Function fun2(){...}
ES6 let const
JavaScript 中无论哪种形式声明(var, let, const, function, function, class)都会存在提升现象。
不同的是 var,function,function 的声明会在提升时进行初始化赋值为 undefined,所以在访问这些变量的时候,不会抛出 ReferenceError 异常
而使用 let,const,class 声明的变量,被提升后不会被初始化,这些变量所处的状态被被称为“temporal dead zone”,此时如果访问这些变量会抛出 ReferenceError 异常,看上去就像没被提升一样。