🎨
Notes
  • 持续更新中...
  • articles
    • browser
      • 深入理解现代浏览器 - 导航
      • 深入理解现代浏览器 - 架构
      • 深入理解现代浏览器 - 交互
      • 深入理解现代浏览器 - 渲染器进程
    • dsa
      • DSA - 并查集
      • DSA - 哈希表
      • DSA - AVL 树
      • DSA - 二叉树
      • 快速选择
      • Big O 算法复杂度
      • DSA - 栈和队列
      • DSA - 前缀树 Trie
      • DSA - 图
      • DSA - 链表
      • DSA - 递归
    • typescript
      • TypeScript 学习笔记 - 任意属性 (Indexable Types)
      • 力扣的 TypeScript 面试题
      • TypeScript 学习笔记 - as const
      • TypeScript 学习笔记 - infer
    • network
      • Internet Protocol (IP)
      • 计算机网络基础
      • 如何分辨同源和同站
      • DNS 如何查询 IP 地址?
    • vue
      • Nuxt.js 入门
      • 从零实现一个 Mini Vue
      • 从零实现一个简单的 VDOM 引擎
      • 从零实现一个响应式状态管理
    • sorting
      • 排序 - 归并排序
      • 排序 - 冒泡排序
      • 排序 - 选择排序
      • 排序 - 计数排序
      • 排序 - 插入排序
    • compile
      • Compiler and Interpreter
      • Just-In-Time (JIT) Compilers
      • 编译流程
    • others
      • 什么是上下文无关语法
      • 如何在终端打印出有颜色的字
    • dev-ops
      • github-actions
        • GitHub Action 简介
        • GitHub Actions for CI
    • workflow
      • 用 Node 写一个 cli
      • 如何规范 git commit 信息
      • 如何监听 git hooks
      • 如何规范代码风格 - prettier
      • 如何发布一个 npm package
      • 如何规范代码质量 - eslint
    • design-pattern
      • 代理模式
      • 单例模式
      • 策略模式
    • security
      • 点击劫持
      • CSP 内容安全策略
    • javascript
      • 尾调用优化
      • 4种常见的内存泄漏及解决方法
    • unit-test
      • Test Vuejs Application - Chapter 2
      • Test Vuejs Application - Chapter 1
      • Vue Unit Test Intro
    • performance
      • HTTP 缓存
      • 如何优化图片资源
Powered by GitBook
On this page
  • 什么是尾调用优化?
  • 尾调用优化的实现
  • 如何分辨尾调用
  • 相关资料

Was this helpful?

  1. articles
  2. javascript

尾调用优化

PreviousjavascriptNext4种常见的内存泄漏及解决方法

Last updated 4 years ago

Was this helpful?

虽然尾调用优化(Tail Call Optimization)是 ES6 规范中的一部分,但其实[大部分 JS 引擎都没有实现]()。

什么是尾调用优化?

正常情况下代码执行时,如果执行到一个函数,就会创建一个执行上下文推入到执行栈中,等到函数执行结束后再将这个执行上下文出栈。

如果我们在一个函数中调用了另一个函数,执行上下文就会在执行栈中一个一个摞起来。

下面是一个简单的递归:

const recursive = n => {
    if (n < 0) return;
    console.log(n);
    recursive(n - 1);
};
recursive(10000);

在执行这段代码的时候,JS 引擎会不断地创建新的 recursive 执行上下文并推入执行栈中,如果 n 足够大,执行栈的内存空间最终会被用完并抛出栈溢出错误。

如果我们仔细观察下上面的代码,会发现在调用了 recursive(n - 1) 之后,recursive(n) 的执行上下文其实已经没用了。因为在 recursive(n - 1) 这个操作之后,已经没有别的代码需要用到 recursive(n) 上下文中的任何变量了。那么,recursive(n) 的执行上下文其实没必要等到 recursive(n - 1) 执行完毕之后再出栈,而是在调用 recursive(n - 1) 的时候就可以出栈了,这就是尾调用优化。

尾调用优化的实现

V8 曾经实现过尾调用优化,但后来又放弃了,主要是因为实现尾调用优化意味着要在代码执行的过程中修改调用栈,这样的话,执行流的信息就不完整了,而且还会导致两个问题:

  1. debug 的时候调用栈信息不完整

如何分辨尾调用

相关资料

的信息会不完整

鉴于这些原因,V8 团队建议修改规范,把 改为 ,也就是要用特定的语法来使用尾调用,比如 return continue func()。目前 V8 还是不支持的,曾经它提供了 --harmony-tailcalls 和 --harmony-explicit-tailcalls 来开启尾调用优化,不过后来又移除了。

error.stack
PTC(Proper Tail Calls)
STC(Syntatic Tail Calls)
https://2ality.com/2015/06/tail-call-optimization.html#checking-whether-a-function-call-is-in-a-tail-position
https://v8.dev/blog/modern-javascript#proper-tail-calls
https://2ality.com/2015/06/tail-call-optimization.html
https://github.com/tc39/proposal-ptc-syntax
https://kangax.github.io/compat-table/es6/#test-proper_tail_calls_(tail_call_optimisation)