JavaScript: let 和 const 提升的区别?

Fla*_*nix 3 javascript node.js ecmascript-6

背景

我有一个函数负责生成一个随机数并使其可用。

"use strict";

module.exports = function(args) {
    let {
        min,
        max,
    } = args;

    let currNumber = genRandom(min, max);

    const genRandom = (min, max) => Math.floor(Math.random() * max) + min;

    const getNumber = () => currNumber;    

    return Object.freeze({
        getNumber
    });
};
Run Code Online (Sandbox Code Playgroud)

问题

出于某种我不明白的原因,当我使用 Node.js 7.8 运行此代码时,出现错误genRandom is not defined.

但是,如果我将代码更改为:

let currNumber = genRandom(min, max);

const genRandom = (min, max) => Math.floor(Math.random() * max) + min;
Run Code Online (Sandbox Code Playgroud)

到:

const genRandom = (min, max) => Math.floor(Math.random() * max) + min;

let currNumber = genRandom(min, max);
Run Code Online (Sandbox Code Playgroud)

那么它的工作原理!

我不明白为什么会发生这种情况。我以为constlet像 一样被吊起来var,但这让我相信我错了。

有人可以向我解释这种行为吗?

T.J*_*der 5

我认为 const 和 let 就像 var 一样被提升,但这让我相信我错了。

不是真的,let而且const确实是被吊起来的,或者我喜欢称之为吊。var fooand之间有两个很大的区别let foo:作用域和初始化。您已经知道范围差异。第二个是 with var foo声明初始化(with undefinedfoo都被提升。随着let,只有声明foo悬挂,而不是初始化foo只是初始化时代码的一步一步执行到达let foo陈述。您不能使用(读取或写入)未初始化的标识符。这段不能使用标识符的时间称为临时死区 (TDZ)。

即使使用var,提升的初始化也是使用 的初始化undefined,而不是 右侧的值=

console.log(typeof foo); // "undefined"
foo();                   // TypeError: foo is not a function
var foo = () => {};
Run Code Online (Sandbox Code Playgroud)

您所做的更改,getRandom在第一次使用它之前将声明向上移动,是正确的做法。(或者使用函数声明,因为整个声明[包括函数的创建] 都被提升了。)

我们来看看这个半吊的东西:

let foo = "outer";
function x()
{
    console.log("...");
    let foo = "inner";
    console.log(foo);
}
x();
Run Code Online (Sandbox Code Playgroud)

let并且const具有块作用域,但我正在使用一个函数,因为我稍后会进行对比var。)

在 内xfoo直到该let foo行才能使用该内部。但是,你不能访问foo它上面的外部;这失败了:

let foo = "outer";
function x()
{
    console.log(foo);    // ReferenceError: `foo` is not defined
    let foo = "inner";
    console.log(foo);
}
x();
Run Code Online (Sandbox Code Playgroud)

这就是半提升:内部的声明foo被提升,但变量直到语句才被初始化let foo。这意味着您根本不能foo在该let foo行上方使用(甚至不能从包含范围使用)。在整个函数中,内部foo遮蔽外部foo,但在初始化之前您不能使用它。规范中的Let 和 Const 声明中对此进行了介绍。

这与var

var foo = "outer";
function x()
{
    console.log(foo);    // undefined
    var foo = "inner";
    console.log(foo);    // "inner"
}
x();
Run Code Online (Sandbox Code Playgroud)

这运行得很好,因为(with )的声明初始化都被提升到函数的顶部。(提升后,该行变成了一个简单的赋值语句。)因此,内部始终遮蔽外部,并且始终可以访问,最初使用其默认值 ( ) 和后面的 with (一旦分配给它)。fooundefinedvar foo = "inner";foofooundefined"inner"

由于 TDZ 是时间性的(与时间相关),而不是空间性的(与范围内的空间或位置相关),因此您可以使用由letconst(或class其声明上方创建的标识符,而不是其声明之前。这会失败,因为它在初始化之前getNumber尝试访问,而它仍在 TDZ 中:theNumber

const getNumber = () => theNumber;

console.log(getNumber()); // ReferenceError

let theNumber = 42;
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为初始化getNumber访问:theNumber

const getNumber = () => theNumber;

let theNumber = 42;

console.log(getNumber()); // 42
Run Code Online (Sandbox Code Playgroud)