每天一点面试题(17)--------JS 高阶函数定义和函数表达式有什么区别?

在 ES 规范中,有一个内部函数 IsAnonymousFunctionDefinition()用来判断一个函数是否为匿名函数,但是这个函数只在规范中使用,通过 JS 代码并不能调用,很多 JS 引擎会在内部实现这个函数(非强制要求)。

当我们谈到匿名函数时,其实有 2 种不同的含义:

函数没有名字
函数的 name 属性是 “anonymous”
由于函数的 name是继承自 Function.prototype.name,因此没有名字的函数的 name 属性是空字符 “”。

在大部分调试工具中,没有名字的函数会显示为 ,也就是我们经常说的匿名函数。

我们把题目中的代码改写一下:

var a = () => () => { throw new Error('foo') }

不论是 a()(),还是 var b = a(); b(),在 Chrome 中执行:
在这里插入图片描述
可以清楚的看到,这个函数是匿名函数。(忽略最下面的匿名函数,那个是 Global Scope )

在 FireFox 的的调用栈都是:
在这里插入图片描述

这就太迷了。不论如何调用 b函数,a都不应该出现在调用栈中,我觉得这应该可以算是 FireFox 的一个 bug 了。

如果使用 new Function定义函数,错误栈里是没有尖括号的。因为根据规范,使用 Function定义的函数是有名字的,名字就是 anonymous。就好比一个人叫无名氏,并不是因为他没有名字,而是他姓无,叫名氏。

在对于这个的处理上,Firefox 略胜一筹吧:

var a = new Function(‘throw new Error(“bar”)’)
a()
FireFox 截图:
在这里插入图片描述

我们再看一下 Chrome Devtools:
在这里插入图片描述

有点迷惑,可能是把引擎内部的调用栈 dump 出来了吧。我觉得这个也可以被认为是一个 bug 了。

题主的疑惑是,为什么 var a = () => () => 1的函数名是 a,而 var b = a()就是匿名函数。

简单解释就是,var a = () => 1包含了 2 个阶段,箭头函数定义和赋值操作。所有的箭头函数都是匿名函数,在箭头函数的定义中,规范内部的 hasName 属性直接设置为了 false。然后IsAnonymousFunctionDefinition(expr) 判定此函数是匿名函数:

If IsFunctionDefinition of expr is false, return false.
Let hasName be HasName of expr.
If hasName is true, return false.
Return true.
当赋值时,会先判断右值是否为匿名函数。如果是的话,重新设置函数名。

我们再来看看另一个语句:var b = a()。 这个也包含了赋值操作,还有一个函数调用操作。

只有匿名函数(静态语义)执行赋值操作时,才会为变量(rval)重新设置 name 属性,即函数名。这里的 a()只是一个函数调用,并不是函数定义, 所以不会重新设置函数名。

类似的:

var a = (1, () => 'foo')
a = [() => 1][0]
var a = eval('()=>1')

都是匿名函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值