声明提前
在js中存在一种机制,在程序正式执行之前,会将var声明的变量和function声明的函数预读到当前作用域的顶部,这就是变量提升和函数提升,这种机制也可以称为变量提升。
例如:
function a(){
console.log(10);
}
a(); //20
function a(){
console.log(20);
}
a(); //20
本意是想通过这个函数分别打印输出10和20,但是由于声明提前,后一个a函数将前面第一个a函数覆盖,导致10无法输出,更要注意的是函数的声明提前是整体提前,而变量只是把声明给提前了,所以为了解决这种全局作用域弊病的问题,建议采用let或const来代替var(let、const是块级作用域),或者采用闭包来解决。<br />通常JS引擎会在正式执行前进行一次“预编译”,就是将变量声明和函数声明提升至当前作用域的顶端,然后再执行。下面将具体介绍JavaScript中的变量提升与函数提升。
console.log(a); // undefined
console.log(fn); // fn(){var b = 2}
console.log(b); // Uncaught ReferenceError: b is not defined
var a = 1;
function fn() {
var b = 2;
};
在预编译之后的代码逻辑如下:
//预编译之后
function fn(){
var b = 2;
}
var a;
console.log(a);
console.log(fn);
console.log(b);
a = 1;
声明提前优先级
“函数会首先被提升,然后才是变量。”——《你不知道的JavaScript》
console.log(foo);
function foo(){
console.log("函数声明");
}
var foo = "变量";
/* 输出为:function foo(){
console.log("函数声明");
} */
预编译之后的实际情况:
function foo(){
console.log("函数声明");
}
var foo
console.log(foo);
foo = "变量";
函数提升优先级⽐变量提升要⾼,且不会被变量声明覆盖,但是会被变量赋值覆盖。
暂时性死区
ES6规定,如果在区块中存在let和const命令(class也有暂时性死区),这个区块对这些命令声明的变量,从这个区块的一开始到它声明之前的这块区域形成了封闭作用域,凡是在这之间(声明之前)使用了这些变量,就会报错。即使用了let或const声明的变量之前,这些变量都是不可用的。这就成为“暂时性死区”。
例如:
var a = 10;
if (true) {
//变量a的暂时性死区 start
console.log(a); // Uncaught ReferenceError: Cannot access 'a' before initialization
//变量a的暂时性死区 end
let a = 20;
}