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:
- Inside a function
- 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
- Is it invoked in new (new binding)? If so, this binds to the newly created object
- Is it called through call, apply (explicit binding) or hard binding? If so, this binds to the specified object.
- Is it called (implicit binding) in a context object? If so, this binds to the context object.
- 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); }