这个例子取自本书 Javascript Allonge 。该主题涉及控制流操作员和功能参数评估。
const or = (a, b) => a || b
const and = (a, b) => a && b
const even = (n) =>
or(n === 0, and(n !== 1, even(n - 2)))
even(42)
//=> Maximum call stack size exceeded.
这本书指出这将导致无限递归。我相信我理解这一部分。由于将评估所有参数,即使或()中的 a 参数为true,仍会评估 b 参数(当在偶数()下调用或()函数,即。这同样适用于和()。 偶数(n - 2)参数最终将被反复评估, n 为2,0,-2,-4 ......
作为一种解决方案,它表示可以将匿名函数作为参数传递。
const or = (a, b) => a() || b()
const and = (a, b) => a() && b()
const even = (n) =>
or(() => n === 0, () => and(() => n !== 1, () => even(n - 2)))
even(7)
//=> false
现在,我理解代码是如何重写的,以便使用包含原始表达式的匿名函数,但我不知道如何延迟评估。"因为匿名函数仍然是或()和 even()函数的参数,所以要阻止它们评估并达到与之前相同的结果码?
答案 0 :(得分:3)
因为匿名函数仍然是or()和even()函数的参数,是什么阻止它们评估并达到与前面代码相同的结果?
确实评估了or()
/ and()
调用的参数。但这意味着函数表达式被计算(到函数),然后传递给or
/ and
,而不是实际调用函数。它仅在函数内部从a()
/ b()
调用,并且仅在实际需要时(由于操作符的短路) - 这就是实际进行递归调用的地方。
顺便说一句,这个概念也称为thunk。
答案 1 :(得分:1)
在even()
的两个版本中,必须在调用or()
之前评估外部调用or()
的参数。因此,在第一个版本中:
const even = (n) =>
or(n === 0, and(n !== 1, even(n - 2)))
必须计算参数表达式,第二个参数将触发无限递归。
然而,在第二个版本中,or()
和and()
函数期望传递返回值的函数,而不是值本身。因此,在代码将其置于or()
和and()
的实现中之前,函数不会被调用。因为编写or()
和and()
函数是为了利用JavaScript短路逻辑运算符,所以没有无限递归。
因此:
const even = (n) =>
or(() => n === 0, () => and(() => n !== 1, () => even(n - 2)))
仍然需要评估or()
的实际参数,但参数只是函数 - 他们说如何获取值,但他们不知道实际做任何事情,直到他们被召唤。