JavaScript:函数执行的顺序

JavaScript:函数执行的顺序,javascript,node.js,ecmascript-6,ecmascript-5,ecmascript-2016,Javascript,Node.js,Ecmascript 6,Ecmascript 5,Ecmascript 2016,我正在学习JavaScript,但有很多东西我不懂。在一次在线JavaScript测试中,出现了以下问题: 以下JavaScript代码将记录到控制台中: const a = {}; const b = () => a.a = () => {}; const c = c => '' + c + a.a(b()); const d = console.log.bind(console); const e = (e => () => d(c(e++)))(0); tr

我正在学习JavaScript,但有很多东西我不懂。在一次在线JavaScript测试中,出现了以下问题:
以下JavaScript代码将记录到控制台中:

const a = {};
const b = () => a.a = () => {};
const c = c => '' + c + a.a(b());
const d = console.log.bind(console);
const e = (e => () => d(c(e++)))(0);

try{
  e();
}catch(a){
  e();
}
我花了一些时间来理解每个变量(这里是常量)代表什么。我从
e()
内部
try
块开始分析代码。因此,
e
表示一个闭包,这意味着函数
d
将被参数
c(0)
调用,
e
将变为
1
。据我所知,这里的
d
基本上表示
console.log
函数(但我不明白他们为什么使用
bind
?)

现在,我知道首先将执行
c(0)
,然后将结果记录到控制台,对吗?让我们看看函数
c
。它返回转换为字符串的第一个参数和
a.a(b())
的连接结果。好的,那么
a.a(b())
将首先执行,对吗?但是,问题是因为
a.a
不是一个函数,它是未定义的,所以会抛出错误,我们转到
catch

现在,在
catch
块中,所有内容都应该是相同的,因此
a.a
仍然不是函数,应该抛出引用错误。但是,当我看到没有抛出错误,但控制台实际上记录了
1未定义的
时,我感到惊讶。为什么?怎么做

好的,经过一点思考,我意识到可能在调用
a.a(b())
时,可能会先执行
b()
。按照我的假设,然后函数
b
作为对对象
a
的属性
a
不起作用的函数的引用,对吗?但是,
a.a
是一个函数,它将在
try
块中执行,并且
0未定义的
将被记录


然而,这两种假设都不正确。这里的主要问题是先执行什么?如果我们调用
someObject.propertyWhichIsNotAFunction(一些生成sitaFunction的东西)
,会发生什么?先执行什么?似乎在
try
块中首先执行一件事,在
catch
块中执行另一件事。这对我来说真的毫无意义。有什么解释吗?

这与错误的原因非常相似,只是有点棘手,因为它依赖于执行中何时抛出类型错误

基本上发生的是:

  • a.a
    将被评估,以确定稍后在步骤3中调用的函数
  • b()
    是经过计算的,因为它是一个函数参数及其返回值 值成为计算到的
    a.a
    的实际参数
  • 执行步骤1中计算的
    a.a
    值,返回值为
    b()
  • 规范的相关部分如下所示:

    请注意,调用函数时发生的第一件事是:

  • 设ref为计算MemberExpression的结果
  • 让func去吧?GetValue(参考)

  • 这意味着对
    a.a
    进行求值,它所引用的函数称为
    func
    。第一次
    a.a
    的计算结果为
    undefined
    ,因此
    func
    undefined
    ,在的步骤2将抛出一个TypeError,它是
    ArgumentListValuation(arguments)
    之后,调用
    b()
    ,并为
    a.a
    指定一个新值,但是不在
    func

    的值之后,这意味着如果
    a.a
    不是函数,那么无论在计算其参数时发生什么,都必须抛出引用错误(除非计算本身抛出错误)?嗯,这很奇怪。我认为JavaScript的设计是为了避免不必要的计算,如
    f1()&&f2()
    如果
    f1()
    的计算结果为false,则不会执行
    f2()
    。我们肯定知道不能调用的函数引用的参数的求值逻辑是什么?@manga171它的参数可能有副作用,就像
    b()
    那样。规范更多地关注正确性而不是优化,短路评估
    f1()&&f2()
    也可能具有正确性含义,例如
    isReady()&&go()
    保证在
    isReady()
    返回
    false
    时不调用
    go()
    ,在调用函数之前不计算函数参数没有这样的好处;相反的问题更有趣:在评估参数之前评估函数引用的逻辑是什么。@manga171对此的答案可能是规范并不完美,对现有代码进行更改是一件大事。很容易显示
    Let argList是什么?ArgumentListValuation(Arguments)。
    总是发生,因此可以将其从任何条件中拉出,并使其成为函数求值时发生的第一件事,但它会改变问题中代码的行为,因为它永远不会引发异常;我认为它将输出
    0undefined
    @Paulpro函数引用在参数之前进行求值,以获得从左到右的求值行为。为什么在计算参数之前不抛出类型错误是一个好问题。@Bergi我认为在计算参数之后抛出类型错误是有意义的,因为它与函数本身在被调用后立即抛出错误的情况是一致的;应用由参数引起的任何副作用。