考虑一下我在其中一个项目中注意到的以下奇怪行为:
async function hello() {
return arguments;
}
当 TypeScript 的编译目标设置为es3
或es5
时,上述文件编译失败,并显示以下错误:
error TS2522: The 'arguments' object cannot be referenced in an async function or method in ES3 and ES5. Consider using a standard function or method.
2 return arguments;
~~~~~~~~~
但是,对于更高的编译目标(我已经测试了es2017
和esnext
),没有错误。
当 TypeScript 的编译目标设置为es3
或es5
时,arguments
关键字阻止它在异步函数中使用是什么?
一些注意事项:
- 在现代 JavaScript 中复制时,此函数不会引发异常
- 此行为只能在
async
函数中复制
我的假设
我怀疑因为Promise
需要在es3
和es5
中多填充,所以填充不能支持arguments
,因为它依赖于函数被调用者。
延伸阅读: ES5.1 规范 § 10.6 参数对象
这是因为异步函数被转译为生成器的基本多填充实现;这个实现基本上交换了函数的主体,将其包装在另一个函数中,因此任何对arguments
的使用都将永远不会访问 hello 的原始arguments
,而是__generator
下面的一个例子是hello,它不接受任何参数并生成以下内容,其中函数的主体被包装在另一个函数中。
async function hello() {
console.log(arguments);
} // becomes.....
function hello() {
return __awaiter(this, arguments, void 0, function () {
return __generator(this, function (_a) {
console.log(arguments);
return [2 /*return*/];
});
});
}
重申一下,console.log(参数)已经从 hello 的上下文转移到了__generator的上下文,这意味着它永远不会按照您的预期运行。如果您的目标是现代浏览器(非IE),则可以将编译目标设置为ES6+,在这种情况下,将删除此限制。
只需使用点差运算符
hello(...args: any[])
现在,您有一个数组或传入的参数。
你是绝对正确的。
我想分享另一种观点,说明为什么这不起作用:
JavaScript具有基于任务的并发性,这意味着代码被分解成小的"块"(任务),并且一次执行其中一个。如果您有将异步任务分成多个任务的内容,则一个用于启动异步操作,另一个用于在异步操作完成后处理结果。其他任务可以同时运行,从而允许并发。
现在,最小的可能执行块是(在async
之前)一个函数:一个函数总是运行到完成,你不能将一个函数拆分为多个任务。
随着async
关键字的引入,async
函数无法完成。它们将被拆分为较小的任务(通过await
)。
现在,如果您必须将async function
转换为常规function
那么您会遇到一个问题:您无法拆分任务。因此,您需要多个函数来表示一个async function
:
async function(arg) { await a(); await b(); }
// becomes
function(arg) { return a().then(function () { b(); }); }
现在可以看到,这并不完全是转译:虽然arg
是唯一async
函数的参数,但只有外部函数具有该arg
。但是,这通常不是问题,无论您是在当前范围内还是在外部范围内访问arg
都不会改变工作方式(除非您重新声明它)。
然而,有一件事被改变了,并构成了这个答案:arguments
.由于我们有两个函数,因此我们有两个arguments
对象。现在也可以模仿arguments
对象,但是您将不得不使用其他不受支持的较新的语言功能(getter/setter,代理)。老实说:你不应该使用arguments
因此翻译它不值得麻烦。