Почему .then() привязана к Promise.resolve() разрешает переназначение объявления const?

Учитывая, что переменная, объявленная с помощью const, не может быть переназначена или удалена, см.

почему можно переназначить значение переменной, объявленной с помощью const в функции, переданной в .then(), прикованной к Promise.resolve(), где передается переменная const, но невозможно переназначить const с функцией, переданной в .then() с привязкой к конструктору Promise, где переменная const передается параметру resolve() конструктора Promise конструктор resolver function?

"use strict"
const state = "123";
Promise.resolve(state)
.then(state => {
  console.log(state); // `"123"`
  state = 456; // reassign `const` variable `state` to `"456"`
  return state
})
.then(state => console.log(state)) // `"456"`
// not reached
.catch(err => console.error(err.message));

Ответ 1

Вы не назначаете переменную const. Вместо этого вы назначаете параметр функции, который вы дали тому же имени. Этот параметр функции является неконстантной копией переменной, поэтому вам разрешено назначать ей.

Я попытаюсь собрать все мои комментарии в более подробный ответ.

В коде здесь:

"use strict"
const state = "123";
Promise.resolve(state).then(state => {
  console.log(state); // `"123"`
  state = 456; // reassign `const` variable `state` to `"456"`
  return state
}).then(state => console.log(state)) // `"456"`
  // not reached
.catch(err => console.error(err.message));

Сначала вы определяете свою переменную const state = "123". Любая попытка изменить содержимое этой точной переменной state вызовет исключение.

Затем, когда вы это сделаете:

Promise.resolve(state).then(state => {

Это объявляет функцию обработчика .then(), которая принимает один аргумент, а имя этого аргумента - state. Когда вызывается обработчик .then(), все, что передается, поскольку этот аргумент обработчику .then() копируется в эту новую переменную аргумента с именем state. Аргументы функции не const. Они могут быть назначены.

Поскольку теперь вы создали две отдельные переменные с одним и тем же именем, и один находится в более высокой области видимости, когда вы находитесь внутри обработчика .then(), этот аргумент функции с именем state "переопределяет" или "скрывает" другой переменная с тем же именем. Когда вы пытаетесь получить доступ к state внутри обработчика .then(), ТОЛЬКО переменная, которую вы можете получить при использовании этого имени, является параметром функции. Этот параметр функции является копией другой переменной состояния в силу передачи в обработчик .then() в качестве аргумента. Все аргументы функции - это копии. Javascript не имеет настоящих ссылочных типов переменных.

Кроме того, аргументы функции не const, поэтому вы можете назначить им.

Итак, когда вы state = "456"; внутри этого обработчика .then(), вы просто назначаете аргумент функции. Поскольку вы создали конфликт имен, на самом деле нет способа получить доступ к переменной с расширенным охватом const state. JS-интерпретатор находит определение, которое ближе всего к области, где вы пытаетесь получить к ней доступ.


Я думаю, что ваша путаница будет устранена, если вы просто прекратите создавать конфликтующее имя переменной. Если вы сделаете это так (назовите параметр localState):

"use strict"
const state = "123";
Promise.resolve(state).then(localState => {
  console.log(state); // `"123"`
  state = 456; // reassign `const` variable `state` to `"456"`
  return state
}).then(state => console.log(state)) // `"456"`
  // not reached
.catch(err => console.error(err.message));

Затем вы увидите исключение при попытке назначить state, потому что вы не создали конфликтующую локальную переменную с таким же именем, чтобы ваша попытка назначить state = 456 действительно пыталась назначить const, и интерпретатор будет объективом.


Насколько я знаю, Javascript не имеет возможности предотвратить переопределение переменной с более высокой областью с новой объявленной переменной с тем же именем в локальной области. Это просто не язык. Когда интерпретатор разрешает имя переменной, он ищет иерархию областей с локального на глобальное, поэтому локальные определения сначала обнаруживаются (и используются). Определения с более высокой степенью охвата "переопределены" или "скрыты" в пределах этой области. Это то, как они разработали разрешение имен имен для работы на языке.

В этом есть много преимуществ, потому что кто-то внезапно объявляет более высокую область видимости, которую вы не используете или даже не подозреваете, никогда не случайно сломает ваши объявления с более низким охватом. Когда вы сами объявляете конфликт, и вы действительно хотите использовать имя с более высоким охватом, это просто ошибка кодирования. Вам не нужно объявлять конфликтующее имя, если вы намерены использовать переменную с более высокой областью с тем же именем.

Ответ 2

Здесь константа state не меняется. Вы меняете значение параметра, которое передается функции разрешения. Вы можете проверить это, просто выполнив console.log(state) после того, как ваш код и вывод будут 123.