JavaScript Scope and Closures

First of all, JavaScript is an interpreted programming language, which needs to be interpreted before the program is executed (a few microseconds before execution). Everyone knows that the scope is searched according to the scope chain, but this general basic conclusion does not allow you to clearly understand the scope and closure. Below we will discuss the details of scope and closure together.

This blog is based on the book "JavaScript You Don't Know" . If there are any mistakes, please correct me.


1. Scope

1. Scope is generally a set of rules for finding variables by name . There are two ways for JS to find variables: LHS and RHS.

LHS (left hand side) can roughly be understood as assigning a value to a variable, on the left side of the assignment symbol (=); RHS (right hand side) refers to finding the source value of a variable, such as on the right side of the assignment symbol (=) .

For example, for a = 10; a = b; these two statements.

var a Here is the LHS query , to find out whether there is a variable a in the current scope to assign a value to a, if so, assign a value; if not, the JS engine will automatically create a variable a to assign a value in non-strict mode; in strict mode In mode, the JS engine will throw a ReferenceError error.

a = b; This statement performs RHS query for the variable b to find the value of the variable b. If the query is successful, the assignment operation is performed; if the query fails, a ReferenceError error is thrown. If the query is successful, but there is a problem with the operation of this variable, a TypeError error will be thrown. For example, b is just an ordinary variable, but it is used as a function (b()).

2.JS is based on lexical scope. It refers to where you write variables and functions when you write code, not the order of calls.

for example

var num = 1;
function a() {
    console.log(num); // 1
}
function b() {
    var num = 2;
    a();
}
b();

When b() is executed in order, a num variable with a value of 2 is defined in the scope of the b function, and then the a function is called in the b function, and the call is made in the scope of the a function (not in the b function domain), find the global variable num = 1.

3. Different scopes will not affect each other, and repeated definitions of variables will not cause errors.

for example

var i = 1;
var j = 2;
function a(){
    var i = 3;
    var j = 4;
    console.log(i,j); // 3,4
}
function b(){
    var i = 5;
    var j = 6;
    console.log(i,j); // 5,6
}
console.log(i,j);// 1,2
a();
b();

4. In the for loop, the variable declared by var can be accessed in the global scope, and the value is the value that exits the loop; the variable declared by let will generate block scope, which will rebind the value to the loop iteration.

for(var i = 0;i<5;i++){
    console.log(i); // 0 1 2 3 4
}
console.log(i); // 5

for(let i = 0;i<5;i++){
    console.log(i); // 0 1 2 3 4
}
console.log(i); // ReferenceError

5. Each scope will be promoted; the function declaration will be promoted, but the function expression will not be promoted;

b() // TypeError
n() // ReferenceError,因为不会变量提升
// 函数声明
function a(){
    console.log(num); // undefined
    var num = 1;
}
// 函数表达式
var b = function n(){
    console.log('b');
}

6. Function declarations are promoted first, followed by variable declarations;

Functions are promoted first, which can be considered as the priority of functions is higher than variables, and variables declared repeatedly will be replaced by functions. But other function declarations can override previous function declarations.

foo(); // 3
var foo = function () {
    console.log(2);
};
 function foo() {
    console.log(1);
}
function foo(){
    console.log(3);
}
foo() // 2

Two, closure

1. Closure is an inevitable result of writing code based on lexical scope. No matter where the function is executed, the function will search the scope chain at the location where the code is written (that is, where the function itself is). When a function does a scope lookup, it forms a closure.

It can also be said that when a function is passed as a value (such as parameter passing, return value, etc.), a closure will be formed.

for example

function foo() {
    var a = 2;
    function bar() {
        console.log(a);
    }
    return bar;
}
var a = 3;
var baz = foo();
baz(); // 2 

When the foo function is executed, the bar function will be returned. When the bar function is executed in the global scope, the RHS will start to search for the value of the a variable along the place where the bar function is defined. At this time, a closure is generated. Closures should be generated precisely because the function is executed outside the scope of the function itself. For IIFE (immediate execution function), although the closure is created, the closure is not actually used, because the execution of the immediate execution function The scope is the scope of the function definition. Although it is a closure, it does not have the function of using the closure.

The function of closure: When the function is executed outside the scope, it will search along the scope chain when the function is defined.

var a = 1;
// 立即执行函数
(
    function(){
        console.log(a);
     }
)();

2. Loops and closures

Observe the example below

for(var i = 0;i<3;i++){
    setTimeout(()=>{
        console.log(i);
    },i*500)
}

The output is 3 3 3, not 0 1 2 as imagined. Because at the end of the for loop, setTimeout starts to execute, and the RHS query of the variable i is performed during execution, but the variable i can only be found in the global scope, which means that the value of the variable i is the value of exiting the loop i = 3;

The result may be slightly different from your imagination. What should I do if I want to output 0 1 2?

(1). Is it feasible to use IIFE to form a closure?

for (var i = 0; i < 3; i++) {
    (function () {
        setTimeout(() => {
            console.log(i);
        }, i*500)
    })()
}

The result is still 3 3 3. Because although the closure is formed by using IIFE, there is no value of i in the closure, and the global i = 3 will be found along the scope chain;

(2). Use a variable in IIFE to temporarily save i value or pass parameters

for (var i = 0; i < 3; i++) {
    (function () {
        var j = i;
        setTimeout(() => {
            console.log(j);
        }, i*500)
    })()
}

// 或
for (var i = 0; i < 3; i++) {
    (function (i) {
        setTimeout(() => {
            console.log(i);
        }, i*500)
    })(i)
}

The result is 0 1 2, the first for loop closure has the variable j, and the second for loop has the formal parameter i, neither of which will reach the global scope.

(3). Use let to generate block scope

for(let i = 0;i<3;i++){
    setTimeout(()=>{
        console.log(i);
    },i*500)
}

The result is 0 1 2, the variable declared by let will be included in the block scope, and as a loop variable, each iteration will declare a variable and save the variable result. When accessing the variable, due to the effect of the closure, it will Get the value for each iteration.

Guess you like

Origin blog.csdn.net/qq_48113035/article/details/123788251