为什么 const arg = arg; 会产生 "Cannot access before initialization" 错误?

Why does `const arg = arg;` produce a "Cannot access before initialization" error?

我发现了我以前从未想过的非常奇怪的行为。我不确定这是否与 TDZ 相关,因为我认为 TDZ 是关于从外部范围到内部范围,而不是像这种情况那样相反。请注意以下示例中的 arg

// Works

const test = {
   func: (arg) => {
      const obj = {
         foo: arg,
      }
      return obj.foo;
   }
}
// Error

const test = arg => {
   {
      const arg = arg; // Cannot access 'arg' before initialization
   }
}

问题是您在函数块中声明了一个 new arg 变量,并且 隐藏了 arg 外部作用域中的变量,例如声明为函数参数的那个。

因此,const arg = arg; 赋值右侧的 arg 引用与左侧引用的变量相同的变量。它是 NOT 引用箭头函数 arg 参数。因此,您完全按照错误所说的去做,在初始化变量之前引用了它(同时尝试初始化它!)。

这可以通过使用唯一名称轻松证明:

const test = arg => {
   {
      const inner_arg = arg;
   }
}

你为什么要使用同一个名字?不仅导致上面的问题,代码也无法阅读。也许您是出于习惯,就像在 class 构造函数中一样?但在那种情况下,您可以使用 this 区分参数和 class 字段,例如this.arg = arg.

错误消息的原因是 letconst 声明都是 block-scoped,这意味着 它们只能在 [=14] 中访问=] 包围着他们。因此,由于 constlet,如果定义块范围内的另一个 variable (arg),则不会访问外部范围的 variable (arg)

或者换句话说: parenthesescurly brackets 中的变量 arg 与传递给函数的 arg 不同,因为你使用letconst 里面。

在解析块作用域时,引擎已经为内部定义的每个变量保留了名称。但是它们只能在使用 const 或 let.

声明和求值后访问。

因此在写入时读取它会导致您看到的错误。

var variable;
{ // [block/env start] 
   let variable = variable; // ReferenceError: Cannot access 'variable' before initialization
} // [block/env end]

let variable = variable 期间发生的事情是它必须在将 value/reference 分配给左侧之前读取右侧,但是根据定义,变量在声明之前不可用,因此它抛出错误。

另一个例子是:

var variable;
{
  console.log(variable); // ReferenceError: Cannot access 'variable' before initialization
  let variable;
}

执行顺序与您示例中的赋值类似,并抛出相同的错误。它不会访问外部 variable 因为另一个 variable 是使用 let/const.

在该块范围内定义的

你也可以看看Let and Const Declarations.

let and const declarations define variables that are scoped to the running execution context's LexicalEnvironment. The variables are created when their containing Environment Record is instantiated but may not be accessed in any way until the variable's LexicalBinding is evaluated. A variable defined by a LexicalBinding with an Initializer is assigned the value of its Initializer's AssignmentExpression when the LexicalBinding is evaluated, not when the variable is created. If a LexicalBinding in a let declaration does not have an Initializer the variable is assigned the value undefined when the LexicalBinding is evaluated.