Почему await и async допустимые имена переменных? - PullRequest
21 голосов
/ 01 мая 2019

Я экспериментировал с тем, как / интерпретируется применительно к разным ключевым словам и операторам, и обнаружил, что следующий синтаксис совершенно допустим:

// awaiting something that isn't a Promise is fine, it's just strange to do:
const foo = await /barbaz/
myFn()

Ошибка:

Uncaught ReferenceError: ожидание не определено

Похоже, что оно пытается проанализироватьawait как имя переменной ..?Я ожидал, что

ожидание действительно только в асинхронной функции

или, может быть, что-то вроде

Неожиданный токен ждет

К моему ужасу, вы даже можете назначить ему вещи:

const await = 'Wait, this actually works?';
console.log(await);

Разве что-то явно неправильное не должно вызывать синтаксическую ошибку, как это происходит с let, finally, break и т. Д.?Почему это разрешено, и что, черт возьми, происходит в первом фрагменте?

1 Ответ

51 голосов
/ 01 мая 2019

Зарезервированные ключевые слова не могут использоваться как идентификаторы (имена переменных) .В отличие от большинства других специальных слов Javascript (например, перечисленных в вопросе, let, finally, ...), await является , а не зарезервированным ключевым словом, поэтому использование его в качестве имени переменной делаетне выбрасывать синтаксическую ошибку.Почему это не было превращено в зарезервированное ключевое слово, когда вышел новый синтаксис?

Обратная совместимость

В 2011 году, когда ES5 был еще относительно новым, код, который использовал awaitasync), поскольку имена переменных были совершенно правильными, поэтому вы могли видеть что-то подобное на нескольких сайтах:

function timeout(ms) {
  var await = $.Deferred();
  setTimeout(await.resolve, ms);
  return await.promise();
};

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

Так что, возможно, если бы они были превращены в зарезервированные ключевые слова, несколько сайтов, которые выбрали бы своеобразное имя переменной, не работали бы должным образом - почему существование этих сайтов должно существоватьнавсегда повлияет на будущее развитие ECMAscript и приведет к запутанному коду, как в вопросе?

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

Вы могли видеть эти взаимодействия в действии с Array.prototype.flatten и Array.prototype.contains - когда браузеры начали их отправку, было обнаружено, что они сломали несколько существующих сайтов из-законфликты имен, поэтому браузеры отказались от реализации, и спецификацию пришлось настроить (методы были переименованы в .flat и .includes).


На самом деле - это ситуация, в которой await не может использоваться в качестве идентификатора, который находится внутри модулей ES6:

<script type="module">
  const await = 'Does it work?';
</script>

Это связано с тем, что в то время как модули ES6 (ES2015) находились в процессе разработки, async / await уже была на горизонте ( начальная фиксация для async / await предложение можно увидеть в начале 2014 года), поэтому при разработке модулей await можно было бы сделать зарезервированным ключевым словом при подготовке к будущему, не нарушая ни одного из существующих сайтов.


Относительно первого фрагмента в вопросе:

const foo = await /barbaz/
myFn()

Это синтаксически допустимо, поскольку await является допустимым именем переменной вне async функций, и интерпретатор считает, что вы пытаетесь делить вместо использования регулярного выражения:

const foo = await / barbaz / myFn()

Если не использовать автоматическую вставку точек с запятой, можно было бы выявить проблему раньше, поскольку последний / не мог быть интерпретирован как деление:

const foo = await /barbaz/;
myFn();

Эта довольно неоднозначная ситуация была фактически специально поднята на заседании ТК39 на async / await:

YK: Что вас беспокоит?

WH: Неопределенности в последовательностях кода, которые начинаются с await /, а затем интерпретируются различными способами (из-заИдентификатор await-as-identifier против разграничения await-as-operator, который переворачивает / между делением и началом регулярного выражения) по грамматикам покрытия по сравнению с реальными грамматиками.Это потенциальная ошибка фермы.

...