ES6 立即调用箭头函数

Posted

技术标签:

【中文标题】ES6 立即调用箭头函数【英文标题】:ES6 immediately invoked arrow function 【发布时间】:2016-04-07 23:13:17 【问题描述】:

为什么这在 Node.js 控制台中有效(在 4.1.1 和 5.3.0 中测试),但在浏览器中无效(在 Chrome 中测试)?

此代码块应创建并调用记录Ok 的匿名函数。

() => 
  console.log('Ok');
()

此外,虽然上述 在 Node.js 中有效,但这不起作用:

n => 
  console.log('Ok');
()

也不是这样:

(n) => 
  console.log('Ok');
()

奇怪的是,当添加参数时,它实际上在立即调用部分抛出了SyntaxError

【问题讨论】:

好问题。两个参数化版本都适用于 Babel 出于兴趣,(n => console.log("Ok"); )(); 有效吗? 是的 (n => console.log("Ok"); )() 甚至可以在 Chrome 开发控制台中使用 等,3 年后,答案是?肯定应该接受以下 3 个答案之一吗?! @joedotnot 我没有得到明确的答案,主要是它在 Node.js 中的一个奇怪的实现。看起来在Node.js 的最新版本中,第一个版本不再工作了。 【参考方案1】:

您需要将其设为 函数表达式,而不是 函数定义,后者不需要名称并使其成为有效的 javascript

(() => 
  console.log('Ok');
)();

相当于IIFE

(function() 
   console.log('Ok');
)();

这在 Node.js 中起作用但在 Chrome 中不起作用的可能原因是它的解析器将其解释为自执行函数,如 this

function()  console.log('hello'); ();

Node.js 中工作正常。这是一个函数表达式,Chrome、Firefox和大多数浏览器都是这样解释的。您需要手动调用它。

告诉解析器期待函数表达式的最广泛接受的方法是将其包装在括号中,因为在 JavaScript 中,括号不能包含语句。此时,解析器遇到function关键字时,就知道将其解析为函数表达式,而不是函数声明。

关于参数化版本,这将起作用。

((n) => 
  console.log('Ok');
)()

【讨论】:

第一个示例在Node.js 中工作,并实际记录了该值。我的问题是它为什么有效?为什么我添加参数时它没有? 我对@9​​87654328@s 非常熟悉,并且知道如何修复我的代码。我只是好奇为什么,例如,我的IIFE 在添加n 参数时不起作用,即使它在没有参数的情况下也起作用。 我没有投反对票,但问题是为什么参数化版本在 Node 中不起作用,而没有参数的完全相同的定义可以 - 它不是询问匿名函数的 Node/Chrome 实现之间的区别 这是一个很好的知识,但它没有回答问题,如前所述, - 当没有参数的完全相同的定义时,为什么参数化版本在 Node 中不起作用 但是箭头函数中function()()的等价物是什么?如果我希望函数对象暴露的函数表达式可以防止这种情况发生。【参考方案2】:

如果没有括号,这些都不能工作。

为什么?

因为按照规范:

    ArrowFunction 是 listed under AssignmentExpression LHS of a CallExpression 必须是 MemberExpressionSuperCallCallExpression

所以 ArrowFunction 不能在 CallExpression 的 LHS 上。


这在解释 => 时的有效含义是,它与赋值运算符 =+= 等在同一级别上工作。

含义

x => foo() 不会变成(x => foo)() 解释器尝试将其解释为x => (foo()) 因此它仍然是一个 SyntaxError 因此解释器确定( 一定是错误的并抛出SyntaxError

Babel 上也有一个关于它的错误 here

【讨论】:

这些是一些有效的观点,但如果我尝试将第一个工作版本替换为:() => (console.log('Ok')()),它将不再有效。所以它并没有真正那样解释它。 @Cristy 这不是有效的箭头函数产生。它认为您正在尝试使用 Object 文字(用括号括起来)创建一个 Object 并且 console.log(...) 不是有效的键名。 @Cristy:是的,我认为上面的解释部分(“含义”位)可能不太正确,但规范部分是据我所知。它也符合我从 V8 得到的错误:SyntaxError: Unexpected token ((指向末尾的() 中的(,而不是console.log(...) 中的()。 @TJCrowder 你是对的,我会删除它,因为它会更改错误消息并且我想说的没有传达(即( 导致口译员在竭尽全力寻找有效解释后放弃,然后说“这一定是错的”),这可能是错误的,因为我不知道解释器的真正编写方式 我想知道如果它不是这个位置的有效令牌,它不会尝试插入分号吗?【参考方案3】:

您遇到此类问题的原因是控制台本身试图模拟您当前所针对的上下文的全局范围。它还尝试从您在控制台中编写的语句和表达式中捕获返回值,以便显示为结果。举个例子:

> 3 + 2
< 5

在这里,它就像一个表达式一样执行,但您已经将它写成一个语句。在普通脚本中,该值将被丢弃,但在这里,代码必须在内部被破坏(比如用函数上下文和 return 语句包装整个语句),这会导致各种奇怪的效果,包括你的问题重新体验。

这也是脚本中的一些裸 ES6 代码可以正常工作但在 Chrome 开发工具控制台中不能正常工作的原因之一。

尝试在 Node 和 Chrome 控制台中执行此操作:

 let a = 3 

在 Node 或 &lt;script&gt; 标签中它工作得很好,但在控制台中,它给出了 Uncaught SyntaxError: Unexpected identifier。它还以VMxxx:1 的形式为您提供了指向源的链接,您可以单击该链接来检查评估的源,显示为:

( let a = 3 )

那么它为什么要这样做呢?

答案是它需要将你的代码转换成一个表达式,以便将结果返回给调用者并显示在控制台中。您可以通过将语句括在括号中来做到这一点,这使它成为一个表达式,但它也使上面的块在语法上不正确(表达式不能有块声明)。

控制台确实尝试通过聪明地处理代码来修复这些边缘情况,但我认为这超出了这个答案的范围。你可以提交一个错误,看看他们是否会考虑修复。

这是一个非常相似的例子:

https://***.com/a/28431346/46588

让您的代码工作的最安全方法是确保它可以作为表达式运行并检查SyntaxError 源链接以查看实际执行代码是什么并从中逆向工程解决方案。通常它意味着一对战略性放置的括号。

简而言之:控制台尝试尽可能准确地模拟全局执行上下文,但由于与 v8 引擎和 JavaScript 语义交互的限制,这有时很难或不可能解决。

【讨论】:

这就是重点,我关心参数,但它不适用于参数集。 好的,我明白你的意思了。不同之处在于 Chrome 开发工具控制台实际执行代码的方式。我将编辑答案以反映这一点。【参考方案4】:

我问了这样一个问题:

@getify 我有一个问题:要生成 #IIFE 模式,我们在函数声明周围使用 parans 将其转换为函数表达式,然后调用它。现在在箭头函数 IIFE 中,为什么我们需要 parans?!箭头函数不是默认已经是表达式了吗?!

这是凯尔辛普森的回答:

一个箭头函数一个表达式,但我们需要围绕“运算符优先级”(排序)的括号 b/c,以便调用箭头-IIFE 的最终括号适用于整个函数而不仅仅是它身体的最后一个标记。

x => console.log(x)(4)    // trouble

(x => console.log(x))(4)    // working

——getify (@getify) June 12, 2020

【讨论】:

我的问题是为什么它适用于某些编译器而不适用于其他编译器。 那是因为不同的编译器在某些细节上表现不同,就像不同的浏览器,当然有不同的编译器 你是对的,它们的行为确实不同,但它们的 JavaScript 规范是相同的。我很好奇哪一个是正确的,JS 规范对这个案例有什么看法,特别是它怎么可能没有争论但没有争论。我正在寻找更具技术性的回应。 你的例子很明显,在第一种情况下它确实应该调用console.log(x)(4) 我只是在这里猜测,但我认为这样解释是非常合理的:在箭头函数表达式中,当我们不使用参数时,我们必须使用括号,这很清楚对于引擎来说,这是一个箭头函数表达式,但是当我们有一个参数时,括号是任意的,这可能不太清楚这是一个函数并混淆了那个引擎,为了解决混淆,我们必须放置一对整个函数表达式的括号

以上是关于ES6 立即调用箭头函数的主要内容,如果未能解决你的问题,请参考以下文章

callapply和bind函数存在的区别

callapply和bind函数存在的区别

es6 箭头函数 this 问题

ES6学习--箭头函数

深入理解ES6箭头函数中的this

箭头函数和 this指向