Почему `const arg = arg;` выдает ошибку «Невозможно получить доступ до инициализации»?

#javascript #variables #scope #arguments #constants

#javascript #переменные #область действия #аргументы #константы

Вопрос:

Я обнаружил действительно странное поведение, о котором никогда раньше не думал. Я не уверен, связано ли это с 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
   }
}
 

Комментарии:

1. Помимо запутанных сообщений об ошибках, зачем пытаться повторно объявить аргумент функции?

2. @Phil Почему нет? Может быть, я хочу сделать его постоянным.

3. Просто чтобы уточнить, вы спрашиваете, почему это ошибка? Я просто пытаюсь отбросить кучу ответов, показывающих вам, как избежать ошибки, не обращаясь к реальному вопросу

4. @Phil уже позаботился.

5. Я обновил вопрос, чтобы, когда он появится в результатах поиска, люди сразу знали, имеет ли это отношение к тому, почему они получают эту ошибку (скорее всего, нет).

Ответ №1:

Причина сообщения об ошибке заключается в том, что let const оба объявления and ограничены областью блоков, что означает, что они доступны только в пределах { } окружающих их. Таким образом, из-за const или let variable (arg) из внешней области не будет доступен, если variable (arg) будет определен другой внутри области блока.

Или другими словами: переменная arg внутри parentheses or curly brackets не совпадает с arg переменной, которую вы передаете функции, потому что вы используете let or const внутри.

При разборе области блока движок уже резервирует имя для КАЖДОЙ переменной, определенной внутри. Но они будут доступны только после объявления и оценки с использованием 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 и Const .

объявления let и const определяют переменные, которые ограничены лексической средой контекста выполнения. Переменные создаются при создании экземпляра содержащей их записи среды, но к ним нельзя получить доступ каким-либо образом, пока не будет вычислена лексическая привязка переменной.Переменной, определенной с помощью LexicalBinding с инициализатором, присваивается значение AssignmentExpression ее инициализатора при вычислении LexicalBinding, а не при создании переменной. Если LexicalBinding в объявлении let не имеет инициализатора, переменной присваивается значение undefined при вычислении LexicalBinding.

Ответ №2:

Проблема в том, что вы объявили новую arg переменную внутри функционального блока, и это скрывает arg переменную во внешней области видимости, например, ту, которая объявлена как параметр функции.

Таким образом arg , в правой части const arg = arg; присваивания ссылается на ту же переменную, что и та, на которую ссылается левая сторона. Он НЕ ссылается arg на параметр функции arrow. Таким образом, вы делаете именно то, что указано в ошибке, ссылаясь на переменную перед ее инициализацией (при попытке ее инициализации!).).

Это легко продемонстрировать, используя уникальные имена:

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

Почему вы все равно используете одно и то же имя? Это не только приводит к вышеупомянутой проблеме, но и к невозможности чтения кода. Возможно, вы делаете это по привычке, как в конструкторе класса? Но в этом случае вы можете различать параметр и поле класса с this помощью , например this.arg = arg .