JS 中的变量提升

JavaScript 中的一切声明 ( var , let , const , function , function* , class ) 均存在变量提升 (hoisted)。
下面的例子中,变量 a 的声明被隐式提升到作用域最顶端并被初始化为 undefineda 并未抛出异常,而未声明的变量 b 抛出 ReferenceError 异常。

console.log(a); // undefined
var a = 1;
console.log(a); // 1
console.log(b); // ReferenceError: b is not defined

var / function / function*let / const / class 的不同在于是否初始化。

varletconst 的「创建」过程都被提升了,但是 letconst 并没有初始化, var 被初始化为 undefined
因此访问 var 声明的变量时,不会报 ReferenceError 异常,而使用 let , const , class 声明的变量,被提升后不会被初始化,这些变量所处的状态被称为暂时性死区 (Temporal Dead Zone, TDZ),此时如果访问这些变量会抛出 ReferenceError 异常,看上去就像没被提升一样。

x = y = "global";
(function() {
    x; // undefined
    y; // Reference error: y is not defined

    var x = "local";
    let y = "local";
}());

函数声明提升

定义函数 声明式

声明式会导致函数提升,function 会被解释器优先编译。即我们用声明式写函数,可以在任何区域声明,不会影响我们调用
函数声明会将声明和赋值都提前,也就是整个函数体都会被提升到作用域顶部:

s(); // 1
function s() {
    console.log(1);
}

定义匿名函数 函数表达式

函数表达式中的 function 则不会出现函数提升(但是赋予的变量会提升)。经由 JS 解释器逐行解释,到了这一句才会赋值函数表达式。因此如果调用在函数表达式之前,则会调用失败。

console.log(s); // undefined
var s = function s() {
    console.log(1);
}
console.log(s); // f s(){console.log(1);}

对于上述定义方式调用 s() 并无区别,但是定义函数会存在函数体的提升,而定义匿名函数时只会将定义变量提升,赋值部分不会提升。

优先级

var s = function() {
    console.log(0);
}

function s() {
    console.log(1);
}
s(); // 0

在 JS 引擎中的执行优先级是 变量声明-->函数声明-->变量赋值

var s; // 变量声明

function s() { //函数声明
    console.log(1);
}

s = function() { // 变量赋值
    console.log(0);
}

s(); // 0

转载规则

《JS 中的变量提升》Konata 采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。
  目录