preface

When first learning to javascript, to say which place which concept is not very good understanding, that I feel the closure is a, but the closure in the front is a very important part of everyday development can say we are used to closure, but we have no attention, at the same time the closure is involved in part of the front end of the interview frequently, So today we are going to review the closure part of the knowledge ~

Concept of pre –

In ES5, scopes are divided into two types: global scope and functional scope. With the arrival of ES6, block-level scopes are added. If you want to better understand closures, it is the first condition to understand scopes

Global scope

We know that for variables, we generally fall into two categories: Global and local variables, defined in the peripheral environment for global variables, defined in the function of the local variable, in a web browser, global variables are generally mounted on the window object, so the global variables can be accessed anywhere, but local variables can only within the scope can be accessed

Let’s use a simple example to understand:

var globalVar = 'Global variable'
function func() {
  console.log(globalVar)    // Global variables
  var localVar = 'Local variable'
  console.log(localVar)    // Local variables
}
func()
console.log(globalVar)     // Global variables
console.log(localVar)     LocalVar is not defined======== split line ================function func() {
  globalVar = 'Global variable'
}
console.log(globalVar)   // Global variables
console.log(window.globalVar)   // Global variables
Copy the code

GlobalVar as a global variable and localVar as a local variable:

  • Global variables have global scope, so they can be accessed anywhere, and on the Web browser side they’re mounted on top of the Window object, right
  • Local variables are defined in a specific local scope and can only be accessed in that scope
  • JS variables that are not directly assigned by definition are global by default (not in strict mode).

Function scope

Function scope, as the name implies, is the scope inside a function. Variables defined inside a function are called function variables, so variables defined inside a function can only be called within that function

Here’s a simple example:

function fun() {
  var funVar = 'Variable in function'
  console.log(funcVar)   // Function variables
}
fun()
console.log(funcVar)   // funcVar is not defined
Copy the code

We can see that a variable we define inside a function can only be accessed inside the function. When the function is finished, the variable is destroyed, so we cannot access it outside the function

Block-level scope

In ES6, the concept of block-level scope was introduced, which states that once a variable is declared in a block using let and const, the block becomes block-level scope (e.g. {} in if/for).

Let’s also look at the code:

console.log(blockVar)    // blockVar is not defined
let blockVar = 'Block-level scope variable'

for (var i=0; i<6; i++) {}
console.log(i)    / / 6

for (let i=0; i<6; i++) {}
console.log(i)   // i is not defined
Copy the code

As you can see from this code, variables in a block-level scope have a “temporary dead zone”, meaning that they cannot be called before definition. Then variables declared with keywords such as let can only be called within their block-level scope and cannot be accessed from outside

closure

Now that we’ve read about scopes, let’s begin to understand closures. Let’s look at some of the concepts of closure packages

Definition of closure

Let’s start with the definition of MDN: a combination of a function bundled with references to its surrounding state (lexical environment) is called 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.

Again from the Little Red Book definition: a closure is a function that has access to a variable in the scope of another function. A common way to create closures is to create another function inside one function

Do you feel better when you don’t see it? Don’t panic, let’s analyze it one by one and sort out the language we can easily understand:

  • “A closure is a function that has access to a variable in the scope of another function” — remove the modifiers and leave only the subject-verb-object, which leads to the conclusion that “a closure is a function.”
  • “Closures allow you to access the scope of an outer function within an inner function” — in plain English: a closure is a function that can access the scope variables inside other functions
  • “A function is bound with references to its surrounding state” — that is, for a closure to form that function, it needs to keep references to the upper scope

A simple conclusion can be drawn from the above: through closures, we can access variables inside a function outside the function

Let’s look at a simple example:

function func1() {
  var a = 'func1'
  return function func2() {
    console.log(a)    // func1
  }
}
func1()()
Copy the code

If we were to use func2, it would not work. It would say that variable A is undefined, but when we combine the closure definition we find that it prints properly, that is, the func1 variable is accessed from func2, Put that together and now do you understand the little Red Book definition of closures?

Why do closures occur

Now that we know the definition and basic concepts of closures, let’s take a closer look at why closures occur. Let’s start with a concept: Scope chain, it is better to understand, such as when we were on a visit to a variable that is within the scope to find first, if not found you’ll find the upper scope up, up until we find or to top layers scope window (web browser side), the entire formation is a chain of the scope chain

Let’s also look at a simple example:

var b = 'Global scope variable'
function func1() {
  var b = 'func1 scoped variable '
  function func2() {
    var b = 'func2 Scoped variable '
    console.log(b)       // func2 Scope variable
  }
  return func2
}
func1()()
Copy the code

If we look at 🌰, func1’s scope points to the global scope and its own scope, while Func2 links itself ->func1 -> the global scope from bottom up

So remember from MDN that “a function is bound with references to its surrounding state”, meaning that the reason for closures is to keep references to the upper scope within the current function

Concrete applications of closures

In the first two parts, we analyzed the main content of closures. In the beginning, we mentioned that closures are very common in our daily development, but maybe we don’t pay much attention to them

We have used timers, event listeners, Ajax requests and other callback functions, which basically use closures. Examples of using timers, such as anti-shock/throttling:

/ / image stabilization
const debounce = (fn,delayTime) = > {
  let timerId, result
  return function(. args) {
    timerId && clearTimeout(timerId)
    timerId = setTimeout(() = >result=fn.apply(this,args),delayTime)
    return result
  }
}
/ / throttling
const throttle = (fn, delayTime) = > {
  let timerId
  return function(. args) {
    if(! timerId) { timerId =setTimeout(() = >{
        timerId = null
        return result = fn.apply(this,args)
      },delayTime)
    }
  }
}
Copy the code

2, IIFE (immediate execution function), this kind of function is special, it has independent scope, does not pollute the global environment, but at the same time can prevent external access to internal variables, so most of the time will be used to do modular or simulate private methods

Here’s an example:

var global = 'Global variable'
let Anonymous = (function() {
  var local = 'Internal variable'
  console.log(global)    // Global variables}) ()console.log(Anonymous.local)   // local is not defined======= split line ==============var global = 'Global variable'
let Anonymous = (function() {
  var local = 'Internal variable'
  console.log(global)    // Global variables
  return {
    afterLocal: local
  }
})()
console.log(Anonymous.afterLocal)   // Internal variables
Copy the code

3. The form in which functions are passed as arguments

var a = 'Global variable'
function func1() {
  var a = 'func1 Internal variable '
  function func2() {
    console.log(a)
  }
  func3(func2)   // func1 Internal variable
}
function func3(fn) {
  // The closure is generated
  fn()
}

func1()
Copy the code

Classic Interview questions

Let’s look at the code first:

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

I’m sure many of us have encountered this problem at one time or another. We print it out and it turns out to be 5 6’s. So why is it this result? So what if I want it to print 12345?

First of all, we will answer why this result is achieved. Before, we mainly talked about it from the perspective of eventLoop. Now we can talk about it from two parts:

  • SetTimeout is a macro task, but JS is a single thread. Due to the eventLoop mechanism, the macro task will be executed only after the synchronized code of the main thread is executed, so all 6 will be printed
  • Because setTimeout is a closure, it refers to the global variable I in the upper scope, and I is already 6, so it prints all 6

So how can we modify it to print the results in order? Here are several common methods:

1. ES6 let: This is the least expensive way to change, because let creates block-level scope, and code execution is done in blocks to meet our requirements

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

2. IIFE (execute function now) : With this method, the variable I is passed to setTimeout each time through the loop

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

3. Use the third parameter of setTimeout: we usually only use the first two parameters, and the third parameter can actually be passed to the function

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

There are also some other clever methods can also achieve this requirement, many methods are not one example, interested in their own research wave ~

This part is not about how difficult it is, but how each person understands it. Everyone has a different way of understanding. When there is nothing wrong, brush the little Red Book together and think about it more

At the end of the article

Finally, if the article is useful to you, welcome to like 👍, pay attention to 😌, thank you ~ also welcome to pay attention to [front-end light and Shadow] public account, get a more systematic and complete front-end module learning!