为什么 await 和 async 是有效的变量名?

Why are await and async valid variable names?

我正在试验 / 在不同关键字和运算符周围的解释方式,发现以下语法是完全合法的:

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

错误:

Uncaught ReferenceError: await is not defined

它似乎试图将 await 解析为 变量名 ..?我在期待

await is only valid in async function

或者类似

Unexpected token await

令我震惊的是,你甚至可以给它赋值:

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

不应该像 letfinallybreak 等那样明显地导致语法错误吗?为什么允许这样做,第一个片段中到底发生了什么?

保留关键字不能用作identifiers (variable names)。与大多数其他特殊 Javascript 单词(如问题中列出的单词 letfinally、...)不同,await 而不是 保留关键字,因此将其用作变量名不会引发 SyntaxError。为什么新语法出来的时候没有把它做成保留关键字?

向后兼容性

回到 2011 年,当 ES5 还是一个相对较新的东西时,使用 await(和 async)作为变量名的代码是完全有效的,所以你可能已经在几个网站:

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

这个变量名的选择可能看起来很奇怪,但是没有错awaitasync 从来都不是保留关键字——如果 ES2017 规范的作者将 await 设为保留关键字,并且浏览器实现了该更改,那么人们在较新的浏览器上访问这些旧网站时会无法使用这些网站;他们很可能会坏掉。

因此,如果将它们设为保留关键字,少数 个选择特殊变量名称的网站将无法正常工作 - 为什么这些网站的存在会永久影响ECMAscript 的未来发展并导致像问题中那样令人困惑的代码?

因为浏览器会拒绝实现破坏现有网站的功能。如果用户发现某个网站无法在一种浏览器上运行,但在另一种浏览器上运行,这将激励他们切换浏览器——第一个浏览器的制造商不希望这样,因为这对他们来说意味着更少的市场份额,即使这是一个使语言更一致和更容易理解的特性。此外,规范的编辑者不想添加永远不会实现(或只会偶尔实现)的内容,因为那样规范将失去其作为标准的某些地位——与其主要目标背道而驰。

您可以通过 Array.prototype.flatten and Array.prototype.contains - when browsers started shipping them, it was found that they broke a few existing sites due to name conflicts, so the browsers backed out of the implementation, and the specification had to be tweaked (the methods were renamed to .flat and .includes) 看到这些互动。


其实一种情况await不能作为标识符,在ES6模块内部:

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

这是因为在研究 ES6 (ES2015) 模块时,async/await 已经在 horizon 上(initial commit for the async/await proposal 可以在2014 年初),因此在设计模块时,可以将 await 设为保留关键字,为将来做准备,而不会破坏任何现有站点。


关于问题中的第一个片段:

const foo = await /barbaz/
myFn()

这在语法上是有效的,因为 awaitasync 函数之外的有效变量名,并且解释器认为您正在尝试 划分 ,而不是使用正则表达式:

const foo = await / barbaz / myFn()

不依赖自动分号插入会更早地发现问题,因为最后一个 / 不能被解释为除法:

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

这种确切的 somewhat-ambiguous 情况实际上是在 async/awaitTC39 meeting 中专门提出的:

YK: What are you worried about?

WH: Ambiguities on code sequences that start with await/ and then get interpreted in diverging ways (due to the await-as-identifier vs await-as-operator distinction that flips the / between division and starting a regexp) by cover grammars vs. real grammars. It's a potential bug farm.