在 JavaScript 中,尾调用是一种函数调用的形式,它出现在函数的最后一个语句,并返回函数结果。尾调用优化就是指在执行这种调用时,JavaScript 引擎会对栈进行优化,使其在调用结束后能够立即释放,并保留当前栈帧中的所有变量。这项优化技术虽然在 ES6 中就已出现,但在 ES7 中继续优化,提供更好的性能和安全性。
尾调用的优点
尾调用的执行会保证传递的参数和返回值都通过栈直接传输。与常规调用不同之处在于,尾调用不会增加函数调用的调用栈。这样可以减少栈的内存占用,并节省计算机资源。即使你有累加或递归的操作,尾调用依然不会将过多的函数变量和参数留在栈中。以下是尾调用的一个示例:
-- -------------------- ---- ------- -------- --------- -- - ------ - - -- - -------- -------------- ----- - ------ ------------ ------ - ----------------------- ----
这个函数定义一个 multiply()
函数,用来相乘两个数字,然后使用 divide()
函数对其进行除法运算。直到最终 multiply()
函数完全执行并返回,整个过程只会有一个新函数添加到栈中。这意味着它会在堆栈中保留最少的信息,在资源方面效率更高。
尾调用的性能提升
尾调用优化在 ES7 中的主要特点之一是可以在 JavaScript 引擎中生成最少的内存占用和最少的中间结果。这个功能简化了程序的优化,降低了它所需的计算时间。以下是尾调用优化的一个示例:
function multiply(n) { if (n <= 1) return 1; return multiply(n - 1) * n; } console.log(multiply(10000));
这个函数使用递归来计算每个数字的乘积,如果不使用尾调用,则需要不断添加新函数来保留每个运算步骤的结果。这个执行方法不利于性能,因为要在堆栈中保留过多的中间结果。但如果将递归改为尾调用,则会大大减少内存占用和执行时间。
如何使用尾调用
尾调用的实现需要遵守一些基本规则。大多数情况下,函数的最后一个语句必须是函数调用,并且调用本身不得进行其他操作。以下是一个有效的尾调用示例:
function multiply(num1, num2) { return divide(num1, num2); }
当然,函数可以包含其他语句以控制算法流程,但是最后一个语句必须是一个函数调用。以下是尾调用的另一个示例:
-- -------------------- ---- ------- -------- ------ -- - ------------- - --- - -------- --------------- ----- ----- - -------------- ------ ------ --------- ------ - ------------ -- ---
这个函数使用 add()
和 multiply()
函数来实现数学计算。在 calculate()
函数中,调用了两个不同的函数,但它们都是作为最后一个语句调用的,因此这是一个有效的尾调用示例。
总结
尾调用优化是 ES7 中新的特性,提供了更简单和更有效的调用方式,并消除了一些内存问题和计算错误。要使用尾调用,需要遵守一些基本规则,使 JavaScript 引擎能够更好地优化这些方案。虽然尾调用的应用范围有限,但还是值得在一些特定情形下使用。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/646736a6968c7c53b07988cb