Introduction to the

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

Scope introduction

  • JavaScript scope, in layman’s terms, refers to the scope within which a variable can be accessed
  • Before ES5, there were only two types of scope in JavaScript: global scope and function scope.
  • ES6 added block-level scopes

Global scope

In programming languages, whether Java or JavaScript, variables are generally divided into global variables and local variables.

  • So variables are defined outside the function, and the first part of the code is usually a global variable.
  • In JavaScript, a global variable is a variable that is mounted under the Window object, so you can use and access the global variable anywhere on the page
  • In JavaScript, all variables that are not defined and assigned directly are global variables by default
  • Now global variables also have global scope, so you can use them wherever you are. When you type window.vName on the browser console, you can access all global variables on the window.
  • Global variables are prone to naming conflicts, so you should pay attention to scope issues when defining variables.
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
Copy the code

Function scope

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

function getName () {
  var name = 'inner';
  console.log(name); //inner
}
getName();
console.log(name);
Copy 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, most directly represented by the new let keyword
  • Variables defined using the let keyword can only be accessed in the block-level scope and have “temporary dead zones”, meaning that the variable cannot be used until it is defined
  • If statement and for statement {… } this includes the block-level scope.
console.log(a) //a is not defined
if(true) {let a = '123';consoleThe (a);/ / 123
}
console.log(a) //a is not defined
// The variable a is in the if statement {... } is defined by the let keyword, so its scope is the part of the if statement in parentheses
Copy the code

What is a closure?

Let’s take a look at closures from the Little Red Book and MDN.

  • A closure is a function that has access to variables in the scope of another function.
  • MDN: A combination of a function that is bound (or surrounded by) references to its surrounding state is a closure. That is, closures allow you to access the scope of an outer function within an inner function.

The basic concepts of closures

  • In layman’s terms, a closure is simply a function that accesses variables inside other functions.
  • That is, a function defined inside a function, or you can just say that the closure is an embedded function.

Normally, variables inside a function cannot be accessed externally (that is, the difference between global and local variables), so the use of closures allows you to access variables inside a function externally and keep their values in memory at all times.

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

// It is clear that a is an internal variable of a fun1 function,
// Normally, as a local variable in a function, it cannot be accessed externally. But with the closure, we can finally get the value of the A variable.
Copy the code

Why closures occur

  • We need to understand the basic concept of scope chains: When accessing a variable, the code interpreter first looks in the current scope. If it doesn’t find one, the code interpreter looks in the parent scope until the variable is found or no parent scope exists.
  • Note that each subfunction copies the parent scope, forming a chain of scopes
var a = 1;
function fun1() {
  var a = 2
  function fun2() {
    var a = 3;
    console.log(a);/ / 3}}// The scope of the fun1 function refers to the global scope (window) and itself;
// The scope of the fun2 function refers to the global scope (window), fun1, and itself;
// The scope is searched from the bottom up until the global scope window is found. An error is reported if the global scope is not found.
Copy the code
  • That is, the current function usually has a reference to the scope of the upper function, so they form a scope chain.

The essence of closure creation is that

  • There is a reference to the parent scope in the current environment.
function fun1() {
  var a = 2
  function fun2() {
    console.log(a);  / / 2
  }
  return fun2;
}
var result = fun1();
result();

// result gets the variable in the parent scope and prints 2.
// Because there are references to fun2 in the current environment, fun2 refers to the scope of window, fun1, and fun2.
// So fun2 is a variable that can access the scope of fun1.
Copy the code

Is it only the return function that generates a closure?

var fun3;
function fun1() {
  var a = 2
  fun3 = function() {
    console.log(a);
  }
}
fun1();
fun3();

// After assigning the fun3 function, the fun3 function has access to the window, fun1, and fun3 scopes;
// Find fun1 from the bottom up until a exists in the scope of fun1;
// So the output is still 2, and the closure has changed.
Copy the code

So whether the result is a function or not does not mean that the closure was not generated.

The representation of closures

  • What are the representations and application scenarios of closures?
  1. Return a function, the reason for which was explained in the previous section and will not be explained here.
  2. Whenever you use a callback function in timers, event listeners, Ajax requests, Web Workers, or any asynchrony, you’re actually using a closure. Take a look at the code below. These are the usual forms of development.
/ / timer
setTimeout(function handler(){
  console.log('1'); },1000);
// Event listener
$('#app').click(function(){
  console.log('Event Listener');
});
Copy the code
  1. Passed as a function parameter, as in the following example.
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 1
Copy the code
  1. IIFE (execute function now) creates a closure that holds the global scope (Window) and the scope of the current function, so it can output global variables, as shown below.
var a = 2;
(function IIFE(){
  console.log(a);  2 / / output}) ();Copy the code
  • The IIFE function is a slightly more special, self-executing anonymous function that has its own scope. Not only does this avoid external access to variables in the IIFE, but it also does not pollute the global scope, which is often seen in advanced JavaScript programming.

Common development application scenarios

  • How to solve the loop output problem?
for(var i = 1; i <= 5; i ++){
  setTimeout(function() {
    console.log(i)
  }, 0)}// The output of this code is 5 6's. The output of this code is 5 6's.
// What if I want you to implement outputs 1, 2, 3, 4, 5?
// Therefore, based on the knowledge we have learned in this lecture, we should think about how to give a satisfactory explanation to the interviewer.
Copy the code
  • 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.
  • 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

Using IIFE (execute function now), each time the for loop passes the variable I to the timer and executes, the modified code looks like this.

for(var i = 1; i <= 5; i++){ (function(j){ setTimeout(function timer(){ console.log(j) }, 0) })(i) }Copy 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

As a commonly used timer, setTimeout has a third parameter. In daily work, we often use the first two parameters, one is the callback function, the other is the time, and the third parameter is seldom used. With the third parameter, the code looks like this.

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

It can be seen that the passing of the third parameter can change the execution logic of setTimeout to achieve the desired result, which is also a way to solve the problem of circular output.

conclusion

  • So that’s pretty much the end of this lecture. In this lecture, I have analyzed the knowledge points related to closures through the combination of principle and practice. As you can see, the knowledge of closures as a whole is quite complex, and it relies on related upstream knowledge.

  • In addition, in the daily front-end development work, developers tend to ignore the systematic learning of this part of the knowledge. In fact, the use of closures often appears in the daily JavaScript programming, especially many and complex scenarios. Because closures keep some variables in memory and do not release them automatically, they can consume a lot of memory if used in large quantities, affecting web page performance. Therefore, it is important that you understand closures more deeply to ensure that the code you deliver performs better.