我正在尝试在不同的关键字和运算符周围如何解释/
,发现以下语法是完全合法的:
// awaiting something that isn't a Promise is fine, it's just strange to do:
const foo = await /barbaz/
myFn()
错误:
未捕获的ReferenceError:未定义等待状态
看起来它试图将await
解析为变量名 ..?我期待中
等待仅在异步功能中有效
或类似的东西
意外的令牌等待中
令我恐惧的是,您甚至可以为其分配东西:
const await = 'Wait, this actually works?';
console.log(await);
不是很明显的错误会引起语法错误,就像let
,finally
,break
等一样吗?为什么允许这样做?第一个代码片段到底是怎么回事?
答案 0 :(得分:42)
保留的关键字不能用作identifiers (variable names)。与大多数其他特殊的Javascript单词(如问题中列出的let
,finally
,...)不同,await
不是 保留关键字,因此将其用作变量名不会引发SyntaxError。当新语法出现时,为什么不将它变成保留关键字?
早在2011年,当ES5还是一个相对较新的事物时,使用await
(和async
)作为变量名的代码是完全有效的,因此您可能已经在夫妇中看到了类似的东西网站:
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
已经出现了(initial commit for the async
/await
proposal可以在2014年初看到),因此在设计模块时,await
可以用作保留关键字,以备将来使用,而不会破坏任何现有站点。
关于问题的第一个片段:
const foo = await /barbaz/
myFn()
这在语法上是有效的,因为await
是async
函数之外的有效变量名,并且解释器认为您正在尝试除,而不是使用常规表达式:
const foo = await / barbaz / myFn()
不依赖自动分号插入会更早地发现问题,因为最后一个/
不能被解释为除法:
const foo = await /barbaz/;
myFn();
这种确切的模棱两可的情况实际上是在async
/ await
上的TC39 meeting中专门提出的:
YK:您担心什么?
WH:以await /开头的代码序列中的歧义,然后以不同的方式进行解释(由于await-as-identifier与await-as-operator的区别导致在除法之间切换/并通过封面语法与真实语法开始一个正则表达式)。这是一个潜在的错误农场。