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)
- Generate variable object, order: create the arguments object – > create function function declarations — > create var variable declarations
- Generate scope chains
- Make sure this points to
- Function execution stage
- 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
- Method 1 calls the function directly
fn()
, which looks like a light rod commander,this
Point to thewindow
(In strict mode, yesundefined
). - Method 2 is a dot call
obj.fn()
At this time,this
Point to theobj
Object. Points in the callthis
Refers to the object before the dot. - In method 3
call
functionfn
In thethis
It points to the first parameter, and here it isobj
. It is usingcall
,apply
,bind
The delta function can take the delta functionthis
The variable points to the first parameter. - Method 4 using
new
Instantiate an objectinstance
, at this momentfn
In thethis
I’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.