JavaScript:函数执行的顺序
我正在学习JavaScript,但有很多东西我不懂。在一次在线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代码将记录到控制台中:
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我认为在计算参数之后抛出类型错误是有意义的,因为它与函数本身在被调用后立即抛出错误的情况是一致的;应用由参数引起的任何副作用。