closure

Think about a few questions

  1. What does scope mean in JavaScript?
  1. In what scenarios will closures be used?
  1. How does the timer loop output the increment of the number through JS code?

Scope introduction

Essentially, it refers to the scope that a variable can access; Before ES5, scopes were divided into two types: global scope and function scope. After ES6, block-level scope appeared

Global scope

In JS, a global variable is a variable that is mounted to the Window object, and can be accessed in any case

var globalName = 'global'; function getName() { console.log(globalName) // global var name = 'inner' console.log(name) // inner } getName(); console.log(name); // console.log(globalName); //global function setName(){ vName = 'setName'; } setName(); console.log(vName); // setName console.log(window.vname) // setName // globalName is a variable that can be accessed anywhere Variables do not have this abilityCopy the code

Advantages: It can be accessed anywhere

Disadvantages: naming conflicts

Function scope

A variable defined in a function is called a function variable, and it can only be accessed from inside the function, so its scope is called the function scope

function getName () { var name = 'inner'; console.log(name); //inner } getName(); console.log(name); // Name is defined in the getName function, so name is a local variable whose scope is in the getName function, also known as function scopeCopy the code

It is not accessible anywhere except inside the function itself. At the same time, the local variable is destroyed when the function is executed. So you can see that the name outside the getName function is not accessible

Block-level scope

ES6 has a new block-level scope, which is most directly expressed by the new LET keyword. Variables defined using the LET keyword can only be accessed in the block-level scope, which has the feature of “temporary dead zone”, that is, the variable cannot be used before the definition

If statement and for statement {… } this includes the block-level scope

Console. log(a) //a is not defined if(true){let a = '123'; The console. The log (a); // 123 } console.log(a) //a is not definedCopy the code

What is a closure?

A closure is simply a function that can access variables inside other functions

function fun1() { var a = 1; return function(){ console.log(a); }; } fun1(); var result = fun1(); result(); / / 1Copy the code

Why closures occur

When accessing a variable, the code interpreter first looks in the current scope. If it doesn’t find one, it looks in the parent scope until the variable is found or no parent scope exists. Such links are called scope chains.

var a = 1; function fun1() { var a = 2 function fun2() { var a = 3; console.log(a); / / 3}}Copy the code

The scope of the fun1 function points to the global scope (window) and itself; The scope of the fun2 function refers to the global scope (window), fun1, and itself; Scope is searched from the bottom up until the global scope Window is found, and an error is reported if the global scope is not present.

Essence: A reference to the parent scope exists in the current environment.

The representation of closures

Return a function

function fun1() {
  var a = 1;
  return function(){
    console.log(a);
  };
}
Copy the code

Whenever you use a callback function in timers, event listeners, Ajax requests, Web Workers, or any asynchrony, you’re actually using a closure

// timer setTimeout(function handler(){console.log('1'); }, 1000); $('#app').click(function(){console.log('Event Listener'); });Copy the code

Passed as a function parameter

var a = 1; function foo(){ var a = 2; function baz(){ console.log(a); } bar(baz); } function bar(fn){// this is the closure fn(); } foo(); // Print 2 instead of 1Copy the code

IIFE (execute the function immediately) creates a closure that holds the global scope (Window) and the scope of the current function, so it can output global variables

var a = 2; (function IIFE(){ console.log(a); })();Copy the code

How to solve the loop output problem?

for(var i = 1; i <= 5; I + +) {setTimeout (function () {the console. The log (I)}, 0)} / / 5,5,5,5,5Copy the code

The reason:

  1. SetTimeout is a macro task. Due to the single-threaded eventLoop mechanism in JS, macro tasks are executed after the simultaneous tasks of the main thread are completed, so the callbacks in setTimeout are executed successively after the loop ends.
  2. The setTimeout function is also a kind of closure, and its parent scope chain is window. Variable I is the global variable of window, and variable I is already 6 before setTimeout is executed, so the final output sequence is all 6

So how do we output 1, 2, 3, 4, 5 in that order?

Using the IIFE

for(var i = 1; i <= 5; I ++){(function(j){setTimeout(function timer(){console.log(j)}, 0)})(I)} // 1, 2, 3, 4, 5Copy the code

Use let in ES6

The new let definition of variables in ES6 has revolutionized JS since ES6, giving JS block-level scope, where code is scoped on a block-level basis. With the modified code, you can achieve the desired results above.

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

The timer passes a third argument

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

\