4
头图
This article will put you in the right perspective on JavaScript closures.
In JavaScript, a closure describes a scenario in which the variables of the outer scope in the function are referenced by the inner scope. The structure of the closure stores the variables of the outer scope for the inner scope.

To understand closures, you must first understand how JS lexical scoping works.

JS lexical scoping

Look at this code:

 let name = 'John';

function greeting() { 
    let message = 'Hi';
    console.log(message + ' '+ name);
}

The variable name is a global variable. It can be called anywhere, including inside the greeting function.

The variable message is a local variable and can only be called inside the greeting function.

If you try to access the message variable from outside greeting(), an error will be thrown:

 ReferenceError: message is not defined

What's more interesting is that the scope inside the function can be nested, as follows:

 function greeting() {
    let message = 'Hi';

    function sayHi() {
        console.log(message);
    }

    sayHi();
}

greeting();
// Hi

The greeting() function creates a local variable message and a local function sayHi().

sayHi() is an internal method of greeting() and can only be accessed inside greeting(). sayHi() has access to greeting()'s message variable. Calling sayHi() inside greeting() prints the value of the variable message.

JavaScript closures (closures)

Let's modify the greeting:

 function greeting() {
    let message = 'Hi';

    function sayHi() {
        console.log(message);
    }

    return sayHi;
}
let hi = greeting();
hi(); // 仍然可以获取到message的值

Instead of executing sayHi() in greeting() this time, we return sayHi as the result when greeting() is called.

Outside the greeting() function, a variable hi is declared, which is the index of the sayHi() function.

At this time, we execute the sayHi() function through this index, and we can get the same result as before.

Under normal circumstances, a local variable only exists when the function is executed. After the function is executed, it will be collected by the garbage collection mechanism.

Interestingly, when we execute hi() in the above way, the message variable will always exist. This is what closures do, in other words the above form is a closure.

Other examples

The following example illustrates a situation where closures are more useful:

 function greeting(message) {
   return function(name){
        return message + ' ' + name;
   }
}
let sayHi = greeting('Hi');
let sayHello = greeting('Hello');

console.log(sayHi('John')); // Hi John
console.log(sayHello('John')); // Hello John

greeting() takes one parameter (message) and returns a function that takes one parameter (name).

The anonymous function returned by greeting concatenates message and name.

In this case greeting() behaves like a factory pattern. Use it to create the sayHi() and sayHello() functions, which both maintain their respective messages "Hi" and "Hello".

Both sayHi() and sayHello() are closures. They share the same function body, but keep different scopes.

Stabilization and throttling

During the interview, the interviewer often asks you to write an anti-shake and throttling function, which is actually a closure.

If you are interested, you can check this article "An example of anti-shake and throttling"

benefits and problems

Advantages of closures

Closures can save the state of variables in their own scope without polluting global variables. Because if many developers develop the same project, it may lead to conflicts of global variables.

Problems that closures can cause

The advantages of closures can become serious problems because variables in closures cannot be collected by GC, especially when closures are used in loops:

 function outer() {
  const potentiallyHugeArray = [];

  return function inner() {
    potentiallyHugeArray.push('Hello'); // function inner is closed over the potentiallyHugeArray variable
    console.log('Hello');
  };
};
const sayHello = outer(); // contains definition of the function inner

function repeat(fn, num) {
  for (let i = 0; i < num; i++){
    fn();
  }
}
repeat(sayHello, 10); // each sayHello call pushes another 'Hello' to the potentiallyHugeArray 
 
// now imagine repeat(sayHello, 100000)

In this example, potentiallyHugeArray will grow infinitely as the number of loops increases, causing Memory Leaks.

Summarize

Closures have both advantages and problems. Only by understanding its principle, can it play the correct role.

The article was first published on IICCOM-personal blog "JavaScript Closure"


来了老弟
508 声望31 粉丝

纸上得来终觉浅,绝知此事要躬行