Series of the opening

Establish clear, precise, essential concepts and clear, precise, necessary connections between them as you enter the front end, so that you can stay calm in whatever interview you’re going to be doing. There is no catalog, but a web of knowledge formed through the association of concepts, as you can see below. When you encounter a relation concept, you can use the parentheses (strong/weak) to determine whether the relation is strongly related to the concept you are understanding (you need to understand before you can proceed) or weakly related (knowledge expansion) to improve your reading efficiency. I also update related concepts regularly.

The interview questions

  • What is a closure
  • What are closures for
  • A variety of pen-based tests based on closure related writing results

What’s this for?

It is best to understand the concept of scope [relational concept (strong)] first.

In fact, to clarify its definition, is basically the core of this article, the definition may need to experience according to the whole article.

Take a look at the various definitions quoted by other officials:

MDN: A combination of a function bound (or surrounded by) references to its surrounding state (lexical environment) is a closure. That is, closures allow you to access the scope of an outer function within an inner function. In JavaScript, whenever a function is created, the closure is created at the same time the function is created.

Little Red Book: Closures are functions that have access to variables in the scope of another function.

Modern JavaScript tutorial: A closure is an inner function that always has access to variables and arguments declared in its outer function, even after its outer function has been returned (end of life).

stackOverFlow: A closure is a persistent scope which holds on to local variables even after the code execution has moved out of that block.

A combination of functions and references to their lexical context. Often used to access a variable indirectly. Closures occur when a function can remember and access its lexical scope, even if the function is executed outside the current lexical scope.

The lexical environment object consists of two parts: execution context -> lexical environment

  • Environment record: An object that stores all local variables as its properties (including some other information, such as the value of this).
  • A reference to an external lexical environment, associated with external code.

If you don’t understand it, you can skip it.

So the simple thing is that when I write the code we decide what the access is to these variables which is thetaLexical scope. However, we can use some means (closures) such asreturnA function. This way, even if the function is executed outside the current lexical scope, it can access variables in the lexical scope that were originally defined (indirectly).

Let’s look at a piece of code that clearly shows closures:

function foo() {
    var a = 2;
    function bar() {
        console.log(a);
    }
    return bar;
}
var baz = foo();
baz(); // 2 That's what closure does.
Copy the code

We observe that baz’s lexical scope is global in terms of lexical scope, and the variable a inside scope of Foo is not accessible externally, but a does print normally.

The lexical scope of the function bar() has access to the internal scope of foo().

We then pass the bar() function itself as a value type.

In this case, the function object referenced by bar is itself the return value. After foo() is executed, its return value (that is, the internal bar() function) is assigned to the variable baz and called baz(), which is really just calling the internal function bar() with different identifier references. Bar () can obviously be executed normally.

But in this case, it executes outside of its own lexical scope.

After foo() is executed, we usually expect the entire internal scope of foo() to be destroyed, because we know that the engine has a garbage collection GC to free up memory space that is no longer used. Since it looks like the content of foo() will no longer be used, it’s natural to consider recycling it.

The magic of closures is that they prevent this from happening. In fact, the inner scope still exists, so it is not reclaimed. Who is using this inner scope? Bar () itself is used. Thanks to the declared location of bar(), it has a closure that covers the inner scope of foo(), making that scope always alive for bar() to reference at any time thereafter. Bar () still holds a reference to that scope.

So, after a few microseconds the variable baz is actually called (calling the inner function bar), and not surprisingly it has access to the lexical scope at definition, so it can also access variable A as expected. This function is called outside the lexical scope of the definition. Closures allow functions to continue to access the lexical scope at definition time.

This allows you to read variables inside the function and keep their values in memory at all times.

No matter how an inner function is passed outside its lexical scope, it holds a reference to the original definition scope and uses closures wherever the function is executed.

I repeated here many times similar words, is to hope that through repetition, give you a deeper impression, the book read more than two times to understand, repetition is an important means to deepen understanding.

Essentially, whenever and wherever you pass functions around as first-level value types, you’ll see closures applied to those functions. Whenever you use callbacks in timers, event listeners, Ajax requests, cross-window communication, Web Workers, or any other asynchronous (or synchronous) task, you’re actually using closures.

How does it work?

Do you write functions or not? Then you’re using it, usually without realizing it. Let’s talk about how to use it in an interview.

Case 1

for (var i = 1; i <= 5; i++) {
    setTimeout(function timer() {
        console.log(i);
    }, i * 1000);
}
Copy the code

Normally, we would expect this code to output the numbers 1 to 5, one per second, one at a time.

But in reality, this code will print 6’s five times at a rate of one per second at run time.

First, explain where the six comes from. The loop terminates if I is no longer <= 5. I is equal to 6 for the first time. Therefore, the output shows the final value of I at the end of the loop. If you think about it, it seems obvious that the callback to a delayed function is executed at the end of the loop.

In fact, when the timer is running even if setTimeout(.. 0), all callback functions are executed at the end of the loop, this is js execution mechanism, so output 6 each time.

This leads to a further question: what are the flaws in the code that cause it to behave differently than the semantics suggest? The flaw is that we try to assume that each iteration in the loop will “capture” itself a copy of I at run time. But according to how scopes work, the reality is that although the five functions in the loop are defined separately in each iteration, they are all enclosed in a shared global scope, so there is really only one I.

With that said, of course all functions share a reference to I. The loop structure lulls us into thinking there are more complex mechanisms at work, but there aren’t. If you define the callback of a delay function five times, without using a loop at all, it is exactly equivalent to this code.

We can solve this problem with closures. First of all, IIFE is a JavaScript function that executes as soon as it is defined, creating closures.

for (var i = 1; i <= 5; i++) {
    (function(j) {
        setTimeout(function timer() {
            console.log(j);
        }, j * 1000);
    })(i);
}
Copy the code

Let is also used for immature IIFE, which essentially converts a block into a scope that can be closed. That is, block-level scope.

for (var i = 1; i <= 5; i++) {
    let j = i; // Yes, block scope for closures!
    setTimeout(function timer() {
        console.log(j);
    }, j * 1000);
}
Copy the code

There is also a special behavior in the let declaration in the header of the for loop. This behavior indicates that the variable is declared not just once during the loop, but every iteration. Each subsequent iteration initializes this variable with the value at the end of the previous iteration. So that’s ok.

for (let i = 1; i <= 5; i++) {
    setTimeout(function timer() {
        console.log(i);
    }, i * 1000);
}
Copy the code

Case 2

Here we create two counters using the same makeCounter function: counter and counter2.

Are they independent? What does the second counter show? 0,1, 2,3 or something else?

function makeCounter() {
  let count = 0;

  return function() {
    return count++;
  };
}

let counter = makeCounter();
let counter2 = makeCounter();

alert( counter() ); / / 0
alert( counter() ); / / 1

alert( counter2() ); // ?
alert( counter2() ); // ?
Copy the code

The answer is: 0,1.

The functions counter and Counter2 are created through separate calls to makeCounter.

Therefore, they have independent external lexical environments, each with its own count.

Example 3

Write a sum function that works like sum(a)(b) = a+b.

Yes, that’s the way through double parentheses (not a mistake).

Here’s an example:

sum(1) (2) = 3
sum(5) (-1) = 4
Copy the code

For the second parenthesis to be valid, the first (parenthesis) must return a function.

function sum(a) {
  return function(b) {
    return a + b; // Get "A" from the external lexical context
  };
}
alert( sum(1) (2));/ / 3
Copy the code

This brings us to the next topic, that of Currying functions.

How does it work?

After we understand the concept, to explore its implementation under the (interview) are often asked about the source code, can someone feel futile, I think it is useful for developing the other associated concepts, will know 】 【 can also take a look at your hard coding ability, to assemble to see how your memory is good. (^ – ^)

Execution context -> Lexical environment -> Example 3

other

new Function

In JavaScript, all functions are inherently closed (with one exception “new Function” syntax)

A Function created using new Function whose [[Environment]] points to the global lexical Environment, not the external lexical Environment of the Function.

Therefore, we cannot use external variables directly in new Function.

See new Function for details

The module pattern

In short, there are two requirements for a modular pattern.

  • There must be an external enclosing function that must be called at least once (each call creates a new module instance).
  • Enclosing functions must return at least one inner function so that the inner function can form a closure in the private scope and can access or modify the private state.

An object with function attributes is not really a module in itself. From a convenience point of view, an object returned from a function call with only data properties and no closure functions is not really a module.


Keep going and you’ll get something.

The above sentence gives you, as well as me, the motivation to move forward.

I am Moore, majoring in mathematics. I have done Internet research and development and testing. I am committed to using technology to change other people’s lives

reference

  • You don’t know Javascript
  • Developer.mozilla.org/zh-CN/docs/…
  • Blog.rccoder.net/javascript/…
  • zh.javascript.info/closure
  • www.cnblogs.com/rubylouvre/…