JS scope, execution context, this, closure is a plite topic, but also novice ignorant knowledge. Of course, even if you are an experienced person, you may not really understand these concepts.

Scope and execution context

Scope:

The scope in JS is lexical, that is, determined by the position at which the function is declared. (Unlike lexical scope, dynamic scope is confirmed at the time the function is executed. Js has no dynamic scope, but JS’s this looks like dynamic scope.) Lexical scope is a set of rules for accessing identifiers within functions that are created at compile time. At the end of the day, a scope is just an “empty disk” with no real variables, but rules that define how variables are accessed.

A scope chain is essentially a list of Pointers to variable objects that refer only to and contain no actual variable objects. A scope chain defines a set of rules for continuing queries along the scope chain when variables are not accessible in the current context.

Execution context:

The execution context is the variable object generated in the execution stack when a function is called. This variable object is not directly accessible, but its variables, this object, and so on can be accessed. Such as:

let fn, bar; Function (x) {let b = 5; fn(x + b); }}}; fn = function(y) { let c = 5; console.log(y + c); //4, fn out of stack, bar out of stack}; bar(10); // enter the bar function contextCopy the code

Each time a function is called, a new execution context is created at the top of the execution stack, and the JavaScript engine handles them as a stack, which we call the Call stack. The bottom of the stack is always the global context, while the top is the currently active execution context, also known as the running Execution Context (blue block), as distinguished from the suspended variable object (execution context) below.

Difference: A scope is a set of access rules for identifiers within a function that are determined at the time the function is declared, while the execution context is the context in which a set of variables is created at the time the function is executed. One is generated at definition time and one is generated at execution time.

Understand how functions are executed

The execution process of the function is divided into two parts. One part is used to generate the execution context, determine the direction of this, declare variables, and generate the scope chain. The other part is to execute the code line by line in order.

  • Establish the execution context phase (occurs when the function is called and before the code inside the function is executed)
  1. Generate variable object, order: create the arguments object – > create function function declarations — > create var variable declarations
  2. Generate scope chains
  3. Make sure this points to
  • Function execution stage
  1. Execute code line by line, variable assignment in case of assignment operation, function reference call in case of function call, condition judgment and expression evaluation in case of condition judgment and expression evaluation, etc

This points to the

As for js’s this keyword, I remember first coming into contact with it when I was working on the front end for six months or a year. , I was confused, what is this? ! From then on officially opened my three years of JS pain journey: encapsulation, closure, object oriented, inheritance and so on. There are only four directions for this:

let fn = function(){
  alert(this.name)
}
let obj = {
  name: ' ',
  fn
}
fn() 1 / / method
obj.fn() 2 / / method
fn.call(obj) 3 / / method
let instance = new fn() 4 / / method
Copy the code
  1. Method 1 calls the function directlyfn(), which looks like a light rod commander,thisPoint to thewindow(In strict mode, yesundefined).
  2. Method 2 is a dot callobj.fn()At this time,thisPoint to theobjObject. Points in the callthisRefers to the object before the dot.
  3. In method 3callfunctionfnIn thethisIt points to the first parameter, and here it isobj. It is usingcall,apply,bindThe delta function can take the delta functionthisThe variable points to the first parameter.
  4. Method 4 usingnewInstantiate an objectinstance, at this momentfnIn thethisI’m pointing to the instanceinstance.

What if more than one rule happens at the same time? In fact, the above four rules have increasing precedence:

fn() < obj.fn() < fn.call(obj) < new fn()

First, the new call has the highest priority, as long as the new keyword is present, this refers to the instance itself; Then, if there is no new keyword and you have call, apply, or bind, this points to the first parameter; Then if there is no new, call, apply, bind, and only obj.foo(), this points to the object in front of the point; Finally, there is the light-rod commander foo(), where this points to window (strictly undefined).

Arrow functions have been added to es6. Arrow functions don’t have their own this, arguments, super, new.target. Arrow functions don’t have a prototype object that can’t be used as a constructor. Since there is no this of its own, this in the arrow function actually refers to this in the containing function. Neither a point call nor a call can change the this in the arrow function.

closure

Js closures are a nightmare for beginners. In the first three years of learning JS, I read countless blog posts searching for the concept of closures, but ultimately found nothing. A closure is defined on MDN as a combination of a function and the lexical environment in which the function is declared.

What? Can you speak English?

For a long time I had a superficial understanding of closures as “functions defined inside a function”. In fact, this is just one of the necessary conditions for closure formation. It wasn’t until I read The definition of closures in The first volume of JAVASCRIPT You Don’t Know by Kyle that I suddenly understood:

Closures occur when a function can remember and access its lexical scope.

let single = (function(){
  let count = 0
  return {
    plus(){
      count++
      return count
    },
    minus(){
      count--
      return count
    }
  }
})()
single.plus() / / 1
single.minus() / / 0
Copy the code

This is a singleton pattern that returns an object and assigns the value to the variable single, which contains two functions plus and minus, both of which use the variable count in their lexical scope. Normally count and its execution context are destroyed at the end of the function execution, but because count is still in use by the external environment, it is not destroyed at the end of the function execution, resulting in closures. Each time single.plus() or single.minus() is called, the count variable in the closure is modified, and both functions retain references to their lexical scope.

A closure is a special function that accesses variables inside a function and keeps the values of those variables in memory and not cleared by garbage collection after a function is called.

Watch a classic Amway:

1 / / method
for (var i = 1; i <= 5; i++) {
  setTimeout(function() {
    console.log(i)
  }, 1000)}2 / / method
for (let i = 1; i <= 5; i++) {
  setTimeout(function() {
    console.log(i)
  }, 1000)}Copy the code

In method 1, five timers are set in a loop. After one second, the timer callback function is executed to print the value of variable I. Needless to say, after a second I had increased to 5, so the timer printed 5 five times. (The timer did not find variable I in the current scope, so I was found in the global scope along the scope chain)

In method 2, as the LET of ES6 will create local scopes, five scopes are set in a cycle, and the distribution of variable I in the five scopes is 1-5. A timer is set in each scope to print the value of variable I after one second. One second later, the timers find variables I 1-5 from their respective parent scopes. This is a new way of using closures to solve the problem of variable exceptions in loops.

The last

I really can’t learn anymore.