[JS basic review] scope, this, closure

Scope

A scope is the accessibility of variables, functions, and objects in certain parts of the runtime code. In other words, the scope determines the visibility of variables and other resources in the code block.

function foo() {
    var a = 1
}
foo()
console.log(a) // Uncaught ReferenceError: inVariable is not defined

The above example can be understood as: the greatest use of scope is to isolate variables. Variables with the same name under different scopes will not conflict.

In JavaScript, scopes can be divided into

  • global scope
  • Function scope

Before ES6, JavaScript had no block level scope, only global scope and function scope. The arrival of ES6 provides us with a 'block level scope', which can be reflected by adding the commands let and const.

Block level scope

The block level scope can be declared through the new command let and const. The declared variables cannot be accessed outside the scope of the specified block. Block level scopes are created when:

  1. Inside a function
  2. Inside a code block wrapped in a pair of curly braces

Scope chain

1. Free variable

First of all, let's know what free variables are. In the following code, console Log (a) wants to get a variable, but a is not defined in the current scope (compare b). There is no variable defined in the current scope, which becomes a free variable. How to get the value of a free variable -- look for the parent scope (Note: this statement is not rigorous and will be explained in detail below).

2. What is scope chain

What if there's no parent? Look up one layer at a time. If the global scope is not found, it will be abandoned. This layer by layer relationship is the scope chain.

let a = 'global';
console.log(a);

function course() {
    let b = 'zhaowa';
    console.log(b);

    session();
    function session() {
        let c = 'this';
        console.log(c);

        teacher();
        function teacher() {
            let d = 'yy';
            console.log(d);

            console.log('test1', b);
        }
    }
}
console.log('test2', b);
course();

if(true) {
    let e = 111;
    console.log(e);
}
console.log('test3', e)

Execution context

Many developers often confuse the concepts of scope and execution context and mistake them for the same concept, but this is not the case.

JavaScript is an interpretive language. The execution of JavaScript is divided into two stages: Interpretation and execution. The things done in these two stages are different:

Interpretation stage:

  • lexical analysis
  • Grammar analysis
  • Scope rule determination

Execution phase:

  • Create execution context
  • Execute function code
  • garbage collection

The scope rules will be determined in the JavaScript interpretation stage, so the scope has been determined when the function is defined, not when the function is called, but the execution context is created before the function is executed. The most obvious thing about the execution context is that the direction of this is determined during execution. The variables accessed by the scope are determined by the structure of the code.

The biggest difference between scope and execution context is:
The execution context is determined at runtime and may change at any time; The scope is determined at the time of definition and will not change.

this

The binding of this actually occurs when the function is called, that is, what this points to depends on how you call the function

this is determined by the dynamic reading context at execution time, not at creation time

Binding rules for this

  • Default binding
function foo() { 
   console.log( this.a );
}
var a = 2; 
foo(); // 2

Default binding: bind the global object to this

Note: in strict mode, global objects cannot use the default binding, that is, an undefined error will be reported during execution

  • Implicit binding

In addition to calling the function directly, in some cases, the call of the function is triggered on an object, that is, there is a context object at the call location.

function foo() { 
    console.log( this.a );
}

var a = 2;

var obj = { 
    a: 3,
    foo: foo 
};

obj.foo(); // 3
  • Implicit loss (callback function)
function foo() { 
    console.log( this.a );
}

var a = 2;

var obj = { 
    a: 3,
    foo: foo 
};

setTimeout( obj.foo, 100 ); // 2

Similarly, although the reference is obj Foo, because it is a reference relationship, what the parameter transfer actually uploads is the reference of the foo object itself. For the call of setTimeout, setTimeout - > get the reference parameter of foo in the parameter - > execute the foo function without obj's participation. The default binding is still performed here.

  • Display binding

call or apply or bind

function foo() { 
    console.log( this.a );
}

var a = 2;

var obj1 = { 
    a: 3,
};

var obj2 = { 
    a: 4,
};
foo.call( obj1 ); // 3
foo.call( obj2 ); // 4

The difference between call, apply and bind

The usage of call is almost the same as that of apply. The only difference is that the parameters passed are different. Call can only be passed in one parameter by one.
apply only supports passing in an array, and even a parameter should be in the form of an array. Finally, when calling the function, the array will be broken into parameters and passed in respectively.

As for the bind method, it directly changes the this point of this function and returns a new function. Later, when calling this function again, this is the first parameter to bind. The method of transferring food by bind is the same as that of call.

Handwritten bind

Function.prototype.myBind = function() {
    const _this = this
    // Copy parameters
    const args = Array.prototype.slice.call(arguments);
    const newThis = args.shift();
    
    
    return function() {
        return _this.apply(newThis, args)    
    }
}

Handwritten apply

Function.prototype.myApply = function(context) {
    // Parameter detection
    context = context || window;
    
    // Point to mount function
    context.fn = this;
    
    // 
}

Binding rule priority

  1. Is it invoked in new (new binding)? If so, this binds to the newly created object
  2. Is it called through call, apply (explicit binding) or hard binding? If so, this binds to the specified object.
  3. Is it called (implicit binding) in a context object? If so, this binds to the context object.
  4. If none, use the default binding. If in strict mode, bind to undefined, otherwise bind to global objects.

Rule exception

function foo() {     console.log( this.a );}foo.call( null ); // 2foo.call( undefined ); // 2

Arrow function

var foo = () => {     
    console.log( this.a );
}

var a = 2;

var obj = { 
    a: 3,
    foo: foo 
};

obj.foo(); //2
foo.call(obj); //2. The binding displayed in the arrow function will not take effect
function foo(){ 
    return () => {
        console.log( this.a );
    }    
}



var a = 2;

var obj = { 
    a: 3,
    foo: foo 
};

var bar = obj.foo();
bar(); //3

closure

What is a closure?

A function is bound with a reference to its surrounding state (lexical environment) (or the function is surrounded by a reference). Such a combination is a closure. In other words, closures allow you to access the scope of its outer function in an inner function. In JavaScript, whenever a function is created, the closure is created at the same time as the function is created.

Closure scenario

  • Function as the return value
 function mail() {
     let content = 'letter';
     return function() {
         console.log(content);
     }
 }
const envelop = mail();
envelop();
  • Function as parameter
// Single responsibility
let content;
// General storage
function envelop(fn) {
    content = 1;

    fn();
}

// Business logic
function mail() {
    console.log(content);
}

envelop(mail);
  • Function nesting
let counter = 0;

function outerFn() {
    function innerFn() {
        counter++;
        console.log(counter);
        // ...
    }
    return innerFn;
}
outerFn()();
  • Event execution
let lis = document.getElementsByTagName('li');

for(var i = 0; i < lis.length; i++) {
    (function(i) {
        lis[i].onclick = function() {
            console.log(i);
        }
    })(i);
}

Reference articles

Keywords: Javascript

Added by fortnox007 on Sun, 20 Feb 2022 19:32:53 +0200