Execute function expressions immediately (IIFE)

The function is enclosed in a line of parentheses () and therefore becomes an expression that can be executed immediately by adding another parentheses () at the end. This pattern is called immediate execution of function expressions, IIFE mode one

var a = 1
(function foo() {
    var a = 2
    console.log(a)  / / 2}) ()console.log(a)  / / 1
Copy the code

Way 2

var a = 1
(function foo() {
    var a = 2
    console.log(a)  / / 2} ())console.log(a)  / / 1
Copy the code

This form has exactly the same function as mode one

The third use is to put the function that needs to be run in the second place and pass it in as an argument after the IIFE is executed. This pattern is widely used in the UMD(Universal Module Definition) project. Although this pattern is rather lengthy, some students think it is easier to understand

var a = 2
(funtion IIFE(def){
    def(window); }) (function def(global){
    var a = 3
    console.log(a)  / / 3
    console.log(global.a)  / / 2
})
Copy the code

The function expression def is defined in the second part of the fragment and is passed as an argument to the first part of the IIFE function definition. Finally, the parameter def is called and the window is passed in as the value of the global parameter

Advanced usage:

IIFE functions can also be called as normal functions and pass arguments to them

var a = 1
(function foo(params) {
    var a = 2
    console.log(a)  / / 2
    console.log(params.a)  / / 1}) (window)
console.log(a)  / / 1
Copy the code

So when you call it, you pass in a Windows object, and the parameter param is actually window

Tail call && tail recursion

The tail call

Definition: when a function returns a call to another function at the last step of its execution, this is called a tail-call expression containing a tail-call:

Conditional operators:? : logic or: | | logic and: && comma:,Copy the code
/ / 1
const a = x= > x ? f() : g();
// Both f() and g() are at the end of the function. Both are tail calls

/ / 2
const a = () = > f() || g();
// g() may be a tail call, but f() is not
// The above is the same as
const a = () = > {
    const fResult = f(); // not a tail call
    if (fResult) {
        return fResult;
    } else {
        return g(); // tail call}}// g() is a tail call when f() executes false

/ / 3
const a = () = > f() && g();
// g() may be a tail call, but f() is not
// Because the above is equivalent to the following:
const a = () = > {
    const fResult = f(); // not a tail call
    if (fResult) {
        return g(); // tail call
    } else {
        returnfResult; }}// G () is a trailing call when f() results in true
Copy the code

Tail call optimization

Normal function call stack:

function baz() {
    bar()
}
function bar() {
    foo()
}
function foo() {
    console.log(12)
}
baz()
Copy the code

The call stack shown in the figure is because when the function is executing, there is no return call when calling another function. The JS engine considers that the execution is not complete, because the call stack will be retained

Optimization: Tail-call optimization only works in strict mode

function baz() {
    return bar()
}
function bar() {
    return foo()
}
function foo() {
    console.log(12)
}
baz()
Copy the code

When a function is executed, when a function call is returned, the current execution stack is popped off the call stack and the called execution stack is pushed into the call stack

Problem solved:

The tail call deletes the outer useless call stack and only saves the inner call stack, saving memory and preventingBurst stack

Tail recursion

Tail recursion: When a function calls itself, it is called recursion as follows:

function foo() {
    foo()
}
Copy the code

Tail recursion: When a function terminally calls itself, it is called tail recursion

function foo() {
    return foo()
}
Copy the code

Note: Both examples have no closing condition and are infinite loops

role

Use recursion to solve factorials

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

factorial(5);            / / 120
factorial(10);           / / 3628800
factorial(500000);       // Uncaught RangeError: Maximum call stack size exceeded
Copy the code

Because the operating system has a size limit for the JS engine call stack allocation, the stack overflow error occurs when the calculated value is too large

Calculate the factorial using tail recursion:

'use strict';

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

factorial(5.1);                / / 120
factorial(10.1);               / / 3628800
factorial(500000.1);           / / points
// Note that although strict mode is enabled here, in both Chrome and Firefox tests, stack overflow errors are reported, and there is no end-of-call optimization
Factorial (500000, 1) results in Infinity because the result is out of JS's range
// For node V6, add the --harmony_tailcalls parameter, node --harmony_tailcalls test.js
// The latest version of node has removed the --harmony_tailcalls feature
Copy the code

Reducing the data complexity from O(n) to O(1) by tail recursion will save a lot of computing time if the data is large enough. As you can see, tail-call optimization is important for recursion. Version 1: Use ES6 defaults

'use strict';
function factorial (num, total = 1) {
    if (num === 1) return total;
    return factorial(num - 1, num * total);
}

factorial(5);                / / 120
factorial(10);               / / 3628800
Copy the code

Improved version 2: Use a function to wrap tail recursive functions

function tailFactorial (num, total) {
    if (num === 1) return total;
    return tailFactorial(num - 1, num * total);
}

function factorial (num) {
    return tailFactorial(num, 1);
}

factorial(5);                / / 120
factorial(10);               / / 3628800
Copy the code