# 27. Tail call optimization
ECMAScript 6 offers *tail call optimization*, where you can make some function calls without growing the call stack. This chapter explains how that works and what benefits it brings.
## 27.1 What is tail call optimization?
Roughly, whenever the last thing a function does is to call another function then the latter does not need to return to its caller. As a consequence, no information needs to be stored on the call stack and the function call is more of a goto (a jump). This kind of call is named *tail call*; not growing the stack is named *tail call optimization* (TCO).
Let’s look at an example to better understand TCO. I’ll first explain how it is executed without TCO and then with TCO.
```function` `id``(``x``)` `{`
`return` `x``;` `// (A)`
`}`
`function` `f``(``a``)` `{`
`const` `b` `=` `a` `+` `1``;`
`return` `id``(``b``);` `// (B)`
`}`
`console``.``log``(``f``(``2``));` `// (C)`
### 27.1.1 Normal execution
Let’s assume there is a JavaScript engine that manages function calls by storing local variables and return addresses on a stack. How would such an engine execute the code?
**Step 1.** Initially, there are only the global variables `id` and `f` on the stack.
![](https://box.kancloud.cn/767bfbbfcbf5d8a40878b0ff0eca869c_1058x299.jpg)The block of stack entries encodes the state (local variables, including parameters) of the current scope and is called a *stack frame*.
**Step 2.** In line C, `f()` is called: First, the location to return to is saved on the stack. Then `f`’s parameters are allocated and execution jumps to its body. The stack now looks as follows.
![](https://box.kancloud.cn/dbfed6e4cbc5469038c252b9a97358a6_1058x541.jpg)There are now two frames on the stack: One for the global scope (bottom) and one for `f()` (top). `f`’s stack frame includes the return address, line C.
**Step 3.**`id()` is called in line B. Again, a stack frame is created that contains the return address and `id`’s parameter.
![](https://box.kancloud.cn/809a6c88cf67d5be4e9db733a4905bb0_1058x729.jpg)**Step 4.** In line A, the result `x` is returned. `id`’s stack frame is removed and execution jumps to the return address, line B. (There are several ways in which returning a value could be handled. Two common solutions are to leave the result on a stack or to hand it over in a register. I ignore this part of execution here.)
The stack now looks as follows:
![](https://box.kancloud.cn/dbfed6e4cbc5469038c252b9a97358a6_1058x541.jpg)**Step 5.** In line B, the value that was returned by `id` is returned to `f`’s caller. Again, the topmost stack frame is removed and execution jumps to the return address, line C.
![](https://box.kancloud.cn/767bfbbfcbf5d8a40878b0ff0eca869c_1058x299.jpg)**Step 6.** Line C receives the value `3` and logs it.
### 27.1.2 Tail call optimization
```function` `id``(``x``)` `{`
`return` `x``;` `// (A)`
`}`
`function` `f``(``a``)` `{`
`const` `b` `=` `a` `+` `1``;`
`return` `id``(``b``);` `// (B)`
`}`
`console``.``log``(``f``(``2``));` `// (C)`
If you look at the previous section then there is one step that is unnecessary – step 5. All that happens in line B is that the value returned by `id()` is passed on to line C. Ideally, `id()` could do that itself and the intermediate step could be skipped.
We can make this happen by implementing the function call in line B differently. Before the call happens, the stack looks as follows.
![](https://box.kancloud.cn/dbfed6e4cbc5469038c252b9a97358a6_1058x541.jpg)If we examine the call we see that it is the very last action in `f()`. Once `id()` is done, the only remaining action performed by `f()` is to pass `id`’s result to `f`’s caller. Therefore, `f`’s variables are not needed, anymore and its stack frame can be removed before making the call. The return address given to `id()` is `f`’s return address, line C. During the execution of `id()`, the stack looks like this:
![](https://box.kancloud.cn/d434f7c067463c713ef2fa44bb555df1_1066x491.jpg)Then `id()` returns the value `3`. You could say that it returns that value for `f()`, because it transports it to `f`’s caller, line C.
Let’s review: The function call in line B is a tail call. Such a call can be done with zero stack growth. To find out whether a function call is a tail call, we must check whether it is in a *tail position* (i.e., the last action in a function). How that is done is explained in the next section.
## 27.2 Checking whether a function call is in a tail position
We have just learned that tail calls are function calls that can be executed more efficiently. But what counts as a tail call?
First, the way in which you call a function does not matter. The following calls can all be optimized if they appear in a tail position:
- Function call: `func(···)`
- Dispatched method call: `obj.method(···)`
- Direct method call via `call()`: `func.call(···)`
- Direct method call via `apply()`: `func.apply(···)`
### 27.2.1 Tail calls in expressions
Arrow functions can have expressions as bodies. For tail call optimization, we therefore have to figure out where function calls are in tail positions in expressions. Only the following expressions can contain tail calls:
- The conditional operator (`? :`)
- The logical Or operator (`||`)
- The logical And operator (`&&`)
- The comma operator (`,`)
Let’s look at an example for each one of them.
#### 27.2.1.1 The conditional operator (`? :`)
```const` `a` `=` `x` `=>` `x` `?` `f``()` `:` `g``();`
Both `f()` and `g()` are in tail position.
#### 27.2.1.2 The logical Or operator (`||`)
```const` `a` `=` `()` `=>` `f``()` `||` `g``();`
`f()` is not in a tail position, but `g()` is in a tail position. To see why, take a look at the following code, which is equivalent to the previous code:
```const` `a` `=` `()` `=>` `{`
`const` `fResult` `=` `f``();` `// not a tail call`
`if` `(``fResult``)` `{`
`return` `fResult``;`
`}` `else` `{`
`return` `g``();` `// tail call`
`}`
`};`
The result of the logical Or operator depends on the result of `f()`, which is why that function call is not in a tail position (the caller does something with it other than returning it). However, `g()` is in a tail position.
#### 27.2.1.3 The logical And operator
```const` `a` `=` `()` `=>` `f``()` `&&` `g``();`
`f()` is not in a tail position, but `g()` is in a tail position. To see why, take a look at the following code, which is equivalent to the previous code:
```const` `a` `=` `()` `=>` `{`
`const` `fResult` `=` `f``();` `// not a tail call`
`if` `(``!``fResult``)` `{`
`return` `fResult``;`
`}` `else` `{`
`return` `g``();` `// tail call`
`}`
`};`
The result of the logical And operator depends on the result of `f()`, which is why that function call is not in a tail position (the caller does something with it other than returning it). However, `g()` is in a tail position.
#### 27.2.1.4 The comma operator (`,`)
```const` `a` `=` `()` `=>` `(``f``()` `,` `g``());`
`f()` is not in a tail position, but `g()` is in a tail position. To see why, take a look at the following code, which is equivalent to the previous code:
```const` `a` `=` `()` `=>` `{`
`f``();`
`return` `g``();`
`}`
### 27.2.2 Tail calls in statements
For statements, the following rules apply.
Only these compound statements can contain tail calls:
- Blocks (as delimited by `{}`, with or without a label)
- `if`: in either the “then” clause or the “else” clause.
- `do-while`, `while`, `for`: in their bodies.
- `switch`: in its body.
- `try-catch`: only in the `catch` clause. The `try` clause has the `catch` clause as a context that can’t be optimized away.
- `try-finally`, `try-catch-finally`: only in the `finally` clause, which is a context of the other clauses that can’t be optimized away.
Of all the atomic (non-compound) statements, only `return` can contain a tail call. All other statements have context that can’t be optimized away. The following statement contains a tail call if `expr` contains a tail call.
```return` `«``expr``»``;`
### 27.2.3 Tail call optimization can only be made in strict mode
In non-strict mode, most engines have the following two properties that allow you to examine the call stack:
- `func.arguments`: contains the arguments of the most recent invocation of `func`.
- `func.caller`: refers to the function that most recently called `func`.
With tail call optimization, these properties don’t work, because the information that they rely on may have been removed. Therefore, strict mode forbids these properties ([as described in the language specification](http://www.ecma-international.org/ecma-262/6.0/#sec-addrestrictedfunctionproperties)) and tail call optimization only works in strict mode.
### 27.2.4 Pitfall: solo function calls are never in tail position
The function call `bar()` in the following code is not in tail position:
```function` `foo``()` `{`
`bar``();` `// this is not a tail call in JS`
`}`
The reason is that the last action of `foo()` is not the function call `bar()`, it is (implicitly) returning `undefined`. In other words, `foo()` behaves like this:
```function` `foo``()` `{`
`bar``();`
`return` `undefined``;`
`}`
Callers can rely on `foo()` always returning `undefined`. If `bar()` were to return a result for `foo()`, due to tail call optimization, then that would change `foo`’s behavior.
Therefore, if we want `bar()` to be a tail call, we have to change `foo()` as follows.
```function` `foo``()` `{`
`return` `bar``();` `// tail call`
`}`
## 27.3 Tail-recursive functions
A function is *tail-recursive* if the main recursive calls it makes are in tail positions.
For example, the following function is not tail recursive, because the main recursive call in line A is not in a tail position:
```function` `factorial``(``x``)` `{`
`if` `(``x` `<=` `0``)` `{`
`return` `1``;`
`}` `else` `{`
`return` `x` `*` `factorial``(``x``-``1``);` `// (A)`
`}`
`}`
`factorial()` can be implemented via a tail-recursive helper function `facRec()`. The main recursive call in line A is in a tail position.
```function` `factorial``(``n``)` `{`
`return` `facRec``(``n``,` `1``);`
`}`
`function` `facRec``(``x``,` `acc``)` `{`
`if` `(``x` `<=` `1``)` `{`
`return` `acc``;`
`}` `else` `{`
`return` `facRec``(``x``-``1``,` `x``*``acc``);` `// (A)`
`}`
`}`
That is, some non-tail-recursive functions can be transformed into tail-recursive functions.
### 27.3.1 Tail-recursive loops
Tail call optimization makes it possible to implement loops via recursion without growing the stack. The following are two examples.
#### 27.3.1.1 `forEach()`
```function` `forEach``(``arr``,` `callback``,` `start` `=` `0``)` `{`
`if` `(``0` `<=` `start` `&&` `start` `<` `arr``.``length``)` `{`
`callback``(``arr``[``start``],` `start``,` `arr``);`
`return` `forEach``(``arr``,` `callback``,` `start``+``1``);` `// tail call`
`}`
`}`
`forEach``([``'a'``,` `'b'``],` `(``elem``,` `i``)` `=>` `console``.``log``(`````${``i``}``. ``${``elem``}`````));`
`// Output:`
`// 0. a`
`// 1. b`
#### 27.3.1.2 `findIndex()`
```function` `findIndex``(``arr``,` `predicate``,` `start` `=` `0``)` `{`
`if` `(``0` `<=` `start` `&&` `start` `<` `arr``.``length``)` `{`
`if` `(``predicate``(``arr``[``start``]))` `{`
`return` `start``;`
`}`
`return` `findIndex``(``arr``,` `predicate``,` `start``+``1``);` `// tail call`
`}`
`}`
`findIndex``([``'a'``,` `'b'``],` `x` `=>` `x` `===` `'b'``);` `// 1`
Next: [28. Metaprogramming with proxies](ch_proxies.html)
- 关于本书
- 目录简介
- 关于这本书你需要知道的
- 序
- 前言
- I 背景
- 1. About ECMAScript 6 (ES6)
- 2. 常见问题:ECMAScript 6
- 3. 一个JavaScript:在 ECMAScript 6 中避免版本化
- 4. 核心ES6特性
- II 数据
- 5. New number and Math features
- 6. 新的字符串特性
- 7. Symbol
- 8. Template literals
- 第9章 变量与作用域
- 第10章 解构
- 第11章 参数处理
- III 模块化
- 12. ECMAScript 6中的可调用实体
- 13. 箭头函数
- 14. 除了类之外的新OOP特性
- 15. 类
- 16. 模块
- IV 集合
- 17. The for-of loop
- 18. New Array features
- 19. Maps and Sets
- 20. 类型化数组
- 21. 可迭代对象和迭代器
- 22. 生成器( Generator )
- V 标准库
- 23. 新的正则表达式特性
- 24. 异步编程 (基础知识)
- 25. 异步编程的Promise
- VI 杂项
- 26. Unicode in ES6
- 27. 尾部调用优化
- 28 用 Proxy 实现元编程
- 29. Coding style tips for ECMAScript 6
- 30. 概述ES6中的新内容
- 注释
- ES5过时了吗?
- ==个人笔记==