Understand this thoroughly

This is one of the most confusing keywords in the JavaScript world. If you don’t understand the nature of this, you will basically be deducted a lot of points by interviewers from first-line Internet companies. After we know the concept of execution context in the previous article, it will be easier to understand the content of this article.

1.1 Why should there be this?

JavaScript allows referring to other variables of the current execution context within the function body.

function func() {
  console.log(a);
}

As in the above code, the function func refers to the variable an of the current execution context. The problem is that this function func can be called in any other execution context, so this a may point to a different one. Because of this, the JS engine needs to have a mechanism that can rely on it Elegantly and accurately point to the context in which the current code is running. Therefore, "this" was born.

What is "elegantly"?

// Suppose there is an object with a long name, and it may be renamed
var iAmALongLongLongNameObject={
name:"bobo",
func1(){
return iAmALongLongLongNameObject.name;
},
func2(){
return this.name;
}
}

The method func2 of AmALongLongLongNameObject uses this keyword. Isn’t that much more elegant? Then even if the object name changes in the future, the code inside func2 does not need to be changed. Func1 can indeed achieve the same function as func2, but it is ugly and inflexible.

What is "accurately"? This can accurately point to (an object) without ambiguity.

// Global variables
var iAmALongLongLongNameObject = {
name: "1989"
};
(function() {
// Local variables
var iAmALongLongLongNameObject = {
name: "coffe",
func1() {
//If you just look at the code, is it easy to see the name of a global variable called?
return iAmALongLongLongNameObject.name;
},
func2() {
//  Just looking at the code here, you can accurately know that the name of the local variable is called!
return this.name;
}
};
console.log(iAmALongLongLongNameObject.func1()); //>> coffe
})();

Unlike "this in high-level languages ​​such as Java points to the instance of the object itself", this in JavaScript points to the object where the function is called, that is, the object that calls the function. You need to know that all functions in JavaScript have properties, just like objects have properties. The function execution phase (that is, the execution phase of the execution context) will get the value of this attribute. At this time, this is a variable that stores the value of the object that calls the function.

var a = "coffe";
function func() {
  console.log(this.a);
}
func();// >> coffe

In the above code, the caller of func is not specified by the dot operator. Then its caller is the default global object window. The fun function is a method of the window, and this.a in its body clearly refers to the attribute a in window, This direction is accurate and clear, there will be no ambiguity. This flexibility of will become very convenient and easy to reuse when designing an API.

1.2 Call location

The call location is the location where the function is called in the code, not the location where it is declared. Study the calling location, that is, find out the question of "who and where called this function". After figuring out the call location, we can accurately find the point of this. To find the calling location, the most important thing is to analyze who and where it was called.

var module = {
x: "1989",
getX: function() {
return this.x;
}
}
console.log(module.getX());//>> 1989
var getX = module.getX;//Note: both getX and module.getX point to the address of the function in memory, they are not "()" for execution
//This is actually the mode of [indirect reference], explanation at the end of the article
console.log(getX()); //>> undefined

As in the above code, to find the call location of the function getX, you need to find out where it is called. Obviously, there are two functions where the function getX() is called. Next, analyze who called it. The getX method as the module object is called. Who calls this situation? Obviously it is called by the object module, and this refers to the module. There is an attribute x in the module object, and its value is 1891, so console.log(module.getX()) outputs 1989. It is called as a global function getX. Who calls this situation? We all know that the global function can be regarded as a method of the window object, so it is obvious that now getX is called as a method of the global object window. After we figure out the call location, we will then proceed to determine the direction of this.

1.3 First look at some misunderstandings that many people point to this

This neither refers to the function itself nor the scope of the function. This was a place that many front-end engineers misunderstood before. Now let's clarify. The point of this is determined when the function is called, that is, when the execution context is created; The point of this has nothing to do with the location of the function declaration, it only depends on the call location of the function (that is, who and where the function is called); Just because the point of this has been determined during the creation phase of the execution context, the this point cannot be changed during the execution phase.

var obj = {
a: "1989"
}
function func() {
this = obj; //An error was reported because trying to modify the point of this during the execution phase
console.log(this.a);
}
func();

1.4 The pointing rule of this

  1. Default pointing Independent function call (when other pointing rules cannot be applied later), this points to the global object window.
function func() {
console.log( this.a ); // this points to the global object
}
var a = 2;
func(); //>> 2

For the default pointing, it is not whether the calling location is in strict mode, but whether the function body is in strict mode. If the function body is in strict mode, this will point to undefined, otherwise, this will point to the global object.

function func() {
"use strict";//The function body is in strict mode, this points to undefined
console.log(this.a);
}
var a = "1989";
(function() {
func(); //>> error
})();
function func() {
console.log(this.a);
}
var a = "1989";
(function() {
"use strict";
func(); //>> 1989
//1989 is output here instead of reporting an error, because in strict mode, the point of this has nothing to do with the calling position of func
})();

There is also a default direction, which is when used in combination with SetTimeout or SetInterval. The code example is as follows.

var num = 0;
class Obj {
constructor(num){
this.num = num;
}
func(){
console.log(this.num);
}
func1(){
setTimeout(function () {
console.log("setTimeout:"+this.num);
}, 1000)
}
func2(){
setInterval(function () {
console.log(this.num);
}, 3000)
}
}
var obj = new Obj(1);
obj.func();//>> 1  The output is obj.num
obj.func1()//>> setTimeout:0  The output is window.num
obj.func2()//>> 0 0 0 0 ……  The output is window.num

It can be found that when the function is passed in setInterval and setTimeout, this in the function will point to the window object.

1.5 Implicitly pointing Implicit orientation is the most common orientation in daily development. The point of this in the function body is determined by the caller at the call location. If the function called by the caller is a method of an object, then when the function is called, its internal this points to the object.

function func1() {
console.log(this.a);
}
var obj = {
a: 6,
func: func1
};
obj.func(); //>> 6
// Find the calling location, call the function func by the obj object,
// At this point, it can be said that when the function func is called, the obj object "owns" or "contains" the func function,
// So at this time this points to the obj object that called the func function.

Only the top or last layer in the object attribute reference chain will affect the call position, that is to say, this points to the object that finally calls the function. This sentence may be more confusing. In fact, in simple and plain terms, this points to the object closest to the called function, and the one that is far away is not. for example:

function func1() {
console.log(this.a);
}
var obj2 = {
a: "1989",
func: func1
};
var obj1 = {
a: "coffe",
obj2: obj2
};
//At this time this points to the obj2 object, because obj2 is close!
obj1.obj2.func(); //>> 1989

Let's look at implicit loss again:

function func1() {
console.log( this.a );
}
var obj = {
a: "coffe1989",
func: func1
};
var bar = obj.func; // Indirect reference, At this time, bar and obj.func are actually

// All point to the function func itself in memory.
var a = "oops, global"; // a is the attribute of the global object window, and it is also a global variable
bar(); //>> oops, global

// Although the bar is a reference to obj.func, in fact, it refers to the func function itself,
// Therefore, bar() at this time is actually an independent function call without any attributives, and the [default pointing] rule is applied,
// So this in the function body points to the window, and this.a points to the attribute an of window (global variable a)

1.6 Explicit pointing The three prototype methods call(), apply() and bind() of the JavaScript built-in object Function, their first parameter is an object, they will bind this object to this, and then let this point to this when calling the function Object.

var a = "bobo";
function func() {
console.log( this.a );
}
var obj = {
a:"bobo coffee"
};
func.call(obj); //>> bobo coffee
//  Force this to bind to obj when calling func

In addition, using bind can modify the this point of SetTimeout and SetInterval.

var num = 0;
class Obj {
constructor(num){
this.num = num;
}
func(){
console.log(this.num);
}
func1(){
setTimeout(function () {
console.log("setTimeout:"+this.num);
}.bind(this), 1000);//bind
}
func2(){
setInterval(function () {
console.log(this.num);
}.bind(this), 2000);//bind
}
}
var obj = new Obj(1);
obj.func();//>> 1  The output is obj.num
obj.func1()//>> setTimeout:1  The output is obj.num
obj.func2()//>> 1 1 1 1 ……  The output is obj.num

1.7 The "new" operator points to

In JavaScript, constructors are just functions that are called when the new operator is used. They will not belong to a certain class, nor will they instantiate a class. In fact, they are not even a special type (class), they are just ordinary functions called by the new operator. Use new to call a function, or when a constructor call occurs, the following operations are automatically performed:

Create (or construct) a brand new object; Assign the scope of the constructor to the new object (so this points to this new object); Execute the code in the constructor (add properties, methods, etc. to this new object); If the function does not return other objects, then return this new object.

function func(a) {
this.a = a;
}
var bar = new func("coffe1989");
console.log(bar.a); //>> coffe1989
// When using new to call func(..), we will construct a new object and bind it to this in the func(..) call