【问题标题】:Why does `const arg = arg;` produce a "Cannot access before initialization" error?为什么`const arg = arg;`会产生“初始化前无法访问”错误?
【发布时间】:2022-01-12 04:50:16
【问题描述】:

我发现了以前从未想过的非常奇怪的行为。我不确定这是否与 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
   }
}

【问题讨论】:

  • 抛开令人困惑的错误消息,为什么要尝试重新声明函数参数?
  • @Phil 为什么不呢?也许我想让它保持不变。
  • 澄清一下,您是在问为什么这是一个错误吗?我只是想阻止一堆答案,向您展示如何在不解决真正的问题的情况下避免错误
  • @Phil 已经处理好了。
  • 我更新了问题,以便当它出现在搜索结果中时,人们会立即知道它是否与 他们 收到该错误的原因相关(很可能不是)。

标签: javascript variables scope arguments constants


【解决方案1】:

错误消息的原因是letconst 声明都是block-scoped,这意味着它们只能在它们周围的{ } 内访问。因此,由于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 期间发生的情况是,它必须在将值/引用分配给左侧之前读取右侧,但根据定义,该变量在声明之前不可用,因此会引发错误。

另一个例子是:

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

执行顺序与您示例中的分配相似,并引发相同的错误。它不会访问外部variable,因为另一个variable 使用let/const 在该块范围内定义。

你也可以看看Let and Const Declarations

let 和 const 声明定义了范围为正在运行的执行上下文的 LexicalEnvironment 的变量。 变量在包含环境记录的实例化时创建,但在评估变量的 LexicalBinding 之前不得以任何方式访问。 由具有 Initializer 的 LexicalBinding 定义的变量被分配其 Initializer 的 AssignmentExpression 的值在评估 LexicalBinding 时,而不是在创建变量时。如果 let 声明中的 LexicalBinding 没有 Initializer,则在评估 LexicalBinding 时,会为变量分配 undefined 值。

【讨论】:

    【解决方案2】:

    问题是你在函数块内声明了一个 new arg 变量,并且 隐藏 arg 变量在外部范围内,例如声明为函数参数的那个。

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

    这很容易通过使用唯一的名称来证明:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-17
      • 1970-01-01
      • 2018-04-17
      • 2020-08-25
      • 2020-09-08
      • 2021-09-29
      • 2019-10-12
      相关资源
      最近更新 更多