closure

This is a topic that came up earlier in my article implementing the context stack

for (var i=0; i<10; i++){
    setTimeout(
        () = >{
            console.log(i)  // Guess the result
        },
        2000)}Copy the code

And the answer is: 10 10s after about 2s

This is because the var keyword has no block-level scope. When the timer is executed asynchronously, the synchronous execution of for has already finished, and I has already become 10. Therefore, the output of all 10 timers is 10

I later solved this problem using a concept introduced in this article, —- closures

for (var i = 0; i < 10; i++) {  // the I of the for loop has no block-level scope and is a global variable
    (function (i) {     // parameter I is a local variable to IIFE
        setTimeout(
            () = > {
                console.log(i)
            },
            2000
        )
    })(i)   // The closure is generated
}
Copy the code

How do closures come about

Let’s leave out what a closure is and what it does. Let’s first look at how closures come about, okay

function func1 () {
    var a = 1   // Break point here
    function func2 () {
        console.log(a)
    }
    func2()     // The closure has already been generated without a call
}

func1() // When executing a function, it will fall at a breakpoint
Copy the code

The closure == is generated when two functions are nested and the inner function references the variables of the outer function

Note that:

  • Variable values can be objects or values of ordinary types
  • The inner function does not need to be called; as long as the variable of the outer function is referenced, the closure is already generated

Finally, we conclude that there are two requirements for closure generation:

  1. Nested function
  2. An inner function refers to a variable of an outer function

What is a closure

Depending on the two conditions required for how closures are generated, we can incidentally say what a closure is —- == a closure is the object that contains the referenced variable ==

Common closures

There are two common closures

  • Take the inner function as the return value of the outer function
  • Pass a function as an argument to another function call

Let’s look at the first:

function func1() {
    var a = 1
    function func2() {
        a++
        console.log(a)
    }
    return func2
}

var getInnerFunc = func1() // Execute the external function to get its return value ---- func2 function
getInnerFunc() / / 2
getInnerFunc() / / 3
getInnerFunc() / / 4
Copy the code

As an aside, how do you count the number of closures generated?

This requires a clear definition of how closures are generated: closures are generated when the inner function is defined (and of course the inner function refers to variables of the outer function).

So, the number of closures generated is the number of times the external function is called. In the example above, the number of closures generated is 1.

In the second case, which may be discouraging for beginners, the Python concept of decorators corresponds to this approach:

function fn1 (fn) {
    var MyName = 'Fitz'
    function wrapper () {   // The inner function is nested within the outer function
        return fn(MyName)   // The closure is generated
    }
    return wrapper
}

// This function takes arguments
function fn2 (a) {
    console.log(a)
}

var decorator = fn1(fn2)
decorator()
Copy the code

The role of closures

So what exactly does a closure do? Since all calls are to be made in the outermost layer (global), why not define them in the global instead of superfluous?

Closures allow variables inside a function to live in memory after the function has completed execution (extending the life of local variables)

function func1() {
    var a = 1
    function func2() {
        a++
        console.log(a)
    }
    return func2
}

var getInnerFunc = func1()
getInnerFunc() / / 2
getInnerFunc() / / 3
Copy the code

The execution context is created when a function is called. Local variables/functions declared in the function will be destroyed after the statement inside the function is executed (the function call is complete)

But the above example is obvious. After the function call ends, we can still access the local variable (function) defined inside the function. Why? So let me draw a picture

The reason: once again, there is a global variable associated with the local func2 corresponding to that function object, so it can be accessed through the global variable getInnerFunc

Since the function object of func2 is still referenced, when the external function func1 and its local variables are collected by the garbage collector, the referenced function object will not be collected (note that the local variables A and func2 are both collected, but the object referred to by func2 is not Reclaim), which can cause the memory leak and overflow problems described below

In addition to extending the declaration cycle for local variables, closures allow outsiders to safely manipulate data inside functions while hiding the implementation from the outside. This is the basis for getter and setter registers in many programming languages

function func1() {
    var a = 1
    function getter() {
        console.log(a)
    }
    function setter(val) {
        a = val
    }
    return {
        get: getter,
        set: setter
    }
}

var getInnerFunc = func1() // Execute the external function to get its return value ---- func2 function
getInnerFunc.get()  / / 1
getInnerFunc.set(Awesome!)
getInnerFunc.get()  / / 666
Copy the code

The life cycle of closures

Generated: Closures are generated when a function is defined and, like a scope, die statically: when the inner function becomes a junk object

function func1 () {
    var a = 1
    function func2 () {
        console.log(a)
    }
    return func2
}

var f = func1()
f()
f = null // This step frees the closure, making the inner function also a garbage object
Copy the code

Application of closures

I’m going to talk a lot about closures, but what does this closure do in practice?

One of the biggest uses of closures is to write js modules ==. The most typical example is Jquery, which is a huge IIFE function that exposes $objects, or Jquery objects, to global objects

Based on closures, we will also make a simple JS module

// Copy the jquery source code
// We customize a mathematical tool method
(function myMath (globalObject) {
    var initVal = 1
    function add (val) {
        return initVal += val
    }
    function pow (val) {
        initVal = initVal ** val
        return initVal
    }

    globalObject.$ = globalObject.fakeJquery = {
        // Es6 object shorthand syntax
        add,
        pow
    }
})(window)
Copy the code
<! DOCTYPEhtml>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
</head>

<body>
    <script src="./myModule.js"></script>
    <script>
        console.log(window)
        console.log($)

        console.log($.add(1))   / / 2
        console.log($.add(2))   / / 4
        console.log($.pow(2))   / / 16
    </script>
</body>
</html>
Copy the code

Disadvantages of closures

Closures are widely used in practice, but they have a significant disadvantage: if the closure is not released in time, it will cause memory leaks, to a certain extent, eventually resulting in memory overflow

Memory leaks

The memory is released immediately after it is used and is occupied by something that is not used

Common memory leaks

Unexpected global variables

function test () {
    a = 'Fitz'  // Forget the keyword declaration, where a is a global variable
}
console.log(a)  // 'Fitz'
Copy the code

A timer that wasn’t cleaned up in time

setTimeout(() = >{
    console.log('hello')},1000)
Copy the code

Manipulate the DOM and its callback functions

const btn = document.getElementById('btn01')
btn.onclick = function () {
    alert('my name is Fitz')
}
btm = null  // Empty the application of variables
document.body.removeChild('btn')    // Also clear references to the DOM
Copy the code

The last is the closure we cover, which causes memory leaks

Out of memory

An overflow occurs when the memory footprint exceeds the total size of available memory

Closure interview questions

Title 1

var name = 'the window'
var object = {
    name: 'my object'.getNameFunc: function () {
        return function () {
            return this.name
        }
    }
}
console.log(object.getNameFunc()()) // 'the window'
Copy the code

This can be a little confusing, but as long as we have a solid knowledge of this, we can not be deceived by the appearance

Here object.getNameFunc()() can actually be written

var innerFunc = object.getNameFunc()
innerFunc() // This naturally points to window
/* This belongs to the concept of implicit binding loss in this */
Copy the code

For this problem, what if we must access the name of object? We need to prevent the implicit binding of this from being lost. We save this of Object ==

var name = 'the window'
var object = {
    name: 'my object'.getNameFunc: function () {
        var that = this
        return function () {
            return that.name
        }
    }
}
console.log(object.getNameFunc()()) // 'my object'
Copy the code

And then there’s the ultimate snakeskin hammer interview question, which is a mind-game, young people

Serving!

function fun(n, o) {
    console.log(o)
    return {
        fun: function (m) {
            return fun(m, n)
        }
    }
}

var a = fun(0)
a.fun(1)
a.fun(2)
a.fun(3)
/* What is the output? * /

var b = fun(0).fun(1).fun(2).fun(3)
/* What is the output? * /

var c = fun(0).fun(1)
c.fun(2)
c.fun(3)
/* What is the output? * /
Copy the code

The answer:

function fun(n, o) {
    console.log(o)
    return {
        fun: function (m) {
            return fun(m, n)
        }
    }
}

var a = fun(0)
a.fun(1)
a.fun(2)
a.fun(3)
/* What is the output? - undefined - 0 - 0 - 0 */

var b = fun(0).fun(1).fun(2).fun(3)
/* What is the output? - undefined - 0 - 1 - 2 */

var c = fun(0).fun(1)
c.fun(2)
c.fun(3)
/* What is the output? - undefined - 0 - 1 - 1 */
Copy the code

Resolution:

function fun(n, o) {
    console.log(o)
    return {
        fun: function (m) {
            return fun(m, n)
        }
    }
}

var a = fun(0)  // There is no argument given to o, so o is undefined

// The resulting object is then assigned to variable A
/* a => { fun: function (m) { return fun(m, 0) // function(m){... } is closure}} */

// the argument 1 is assigned to the parameter m
a.fun(1)  // Execute fun(n=1, o=0) and print o=0
// the argument 2 is assigned to the parameter m
a.fun(2)  // Execute fun(n=2, o=0) and print o=0
// the argument 3 is assigned to parameter m
a.fun(3)  // Execute fun(n=3, o=0) and print o=0
/* What is the output? - undefined - 0 - 0 - 0 */
Copy the code

Since a.sun () is three separate calls, resulting in different execution contexts, variables between functions are independent and have no memory

Then,

function fun(n, o) {
    console.log(o)
    return {
        fun: function (m) {
            return fun(m, n)
        }
    }
}
var b = fun(0).fun(1).fun(2).fun(3)

/* fun(0): n=0 o=undefined undefined fun(0).fun(1): m=1 M = 2 n = last m = 1 output 1 fun (0) fun (1) fun (2) fun (3) : m = 3, n = m = 2 o 2 * / last time

/* What is the output? - undefined - 0 - 1 - 2 */
Copy the code

The execution context object is always the same because of successive calls, so the variables/parameters after the previous call, which affect the results of the subsequent call, are remembered

The last

function fun(n, o) {
    console.log(o)
    return {
        fun: function (m) {
            return fun(m, n)
        }
    }
}

var c = fun(0).fun(1)
/* fun(0): n=0 o=undefined undefined fun(0). Fun (1): m=1 function (m) { return fun(m, 1) } } */

c.fun(2)
Function (2) {return fun(2, 1)} 1 */

c.fun(3)
Function (3) {return fun(3, 1)} 1 */


/* What is the output? - undefined - 0 - 1 - 1 */
Copy the code

This example is a combination of the above two, with successive calls to the C object, followed by separate calls to the C object, examining the execution context object and the obvious closure