This is the 29th day of my participation in Gwen Challenge

The tail call

The ES6 specification adds a memory-management optimization that allows the JavaScript engine to reuse the frame stack when conditions are met. This optimization is great for tail-call code.

So what is a tail call? To simplify this, a function executes another function when it returns, as shown below:

function innerFunction() {
    console.log('inner')};function outerFunction() {
    console.log('outer');
    return innerFunction(); / / end call
}

outerFunction();
Copy the code

The execution of a function forms an “call frame” in memory, holding information such as the call location and internal variables. Call frames for multiple functions are stored as stacks, called “call stacks.”

Prior to ES6 optimization, the above code would do the following in memory:

  1. The outerFunction function is executed and the function call frame is pushed onto the call stack
  2. To execute a return statement, you must first execute the innerFunction function to get the return value
  3. The innerFunction function is executed, and the function call frame is pushed into the call stack, which contains two function call frames
  4. The innerFunction body is executed, and the return value is passed to the outerFunction. The outerFuntion returns the value
  5. The call frame is popped off the stack

After ES6, this code operates in memory as follows:

  1. The outerFunction function is executed and the function call frame is pushed onto the call stack
  2. To execute a return statement, you must first execute the innerFunction function to get the return value
  3. The engine finds it’s okay to pop the first call frame off the stack because the innerFunction return value is also the outerFunction return value
  4. Eject the outerFunction call frame
  5. Execute the innerFunction function, and the function call frame is pushed into the call stack, where there is only one function call frame
  6. After executing the innerFunction body, calculate its return value
  7. Pop the innerFunction call frame off the stack

So what does it take to make a “tail call”? The external function call frame does not need to exist under the following conditions:

  1. The code executes in strict mode. Because in normal mode, there are two properties inside the function that trace the call stack of the function. Neither attribute is available in strict mode.
    arguments: returns the argument to the function when it is called. Caller: Returns the function that calls the current function.Copy the code
  2. The return value of an external function is a call to the tail-calling function;
  3. No additional logic needs to be performed after the tail-calling function returns;
  4. Tail-calling functions are not closures that reference free variables in the scope of an external function.

Tail recursion

The function calls itself, called recursion. If the tail calls itself, it is called tail recursion.

Recursive code needs to hold hundreds or thousands of call frames in memory at the same time, making it prone to “stack overflow” errors. According to the above, if the recursive code can be written in the form of tail call, the call frame of the outer function can be popped out of the stack in advance, so that there is only one call frame in the call stack forever, thus avoiding the problem of “stack overflow” caused by recursion.

function factorial(n) {
  if (n === 1) return 1;
  return n * factorial(n - 1);
}

factorial(5) / / 120
Copy the code

The above code implements factorial recursively by holding up to n function call frames in memory at the same time. To write tail recursion, you only need to save a function call frame.

function factorial(n, total) {
  if (n === 1) return total;
  return factorial(n - 1, n * total);
}

factorial(5.1) / / 120
Copy the code

The relevant data

JavaScript advanced programming

Tail-call optimization