Detailed explanation of this in JavaScript

  • 2021-07-06 09:40:09
  • OfStack

In fact, this is an old-fashioned problem. There are many articles about this, In fact, I thought I had figured it out earlier, but in the process of doing the project yesterday, there was still a trace of doubt. I thought of an article about this (with a link later and a Chinese translation on rare earth) and another article recommended by a predecessor, and my understanding of this really improved a little.

'this' in JavaScript is dynamic and is determined when the function is running rather than when the function is declared. All functions can call 'this', regardless of whether the function belongs to an object. There are four main situations about this.

1. Methods that are called as objects

If the function is treated as a method of some 1 object, then the this of the function points to that object;


  var john = {
   firstName: "John"
  }
  function func() {
   alert(this.firstName + ": hi!")
  }
  john.sayHi = func
  john.sayHi() // this = john

It is worth noting here that when the method of an object is taken out and assigned to a variable, the method becomes a function trigger, and this points to window or underfind (strict mode).

2. Call within a function

When there is this in the function, it actually means that it is called as a method, and calling between them is equivalent to treating it as a method of window object. this points to window. It is worth noting that ES5 actually stipulates this situation. this=undefined, only browsers mostly execute according to the old method (my tests in the latest version of Chrome, Safari and Firefox all point to window (201607))


  func()
  function func() {
    alert(this) // [object Window] or [object global] or kind of..
  }

To pass this, () should be a reference type, similar to obj. a or obj ['a'], nothing else.

There is still a small pit here. When there is a function in the method of the object, the function is actually triggered as a function mode, so its this defaults to window (undefined in strict mode). The solution is to bind this to the function.


var numbers = { 
  numberA: 5,
  numberB: 10,
  sum: function() {
   console.log(this === numbers); // => true
   function calculate() {
    // this is window or undefined in strict mode
    console.log(this === numbers); // => false
    return this.numberA + this.numberB;
   }
   return calculate();
  }
};
numbers.sum(); // => NaN or throws TypeError in strict mode 
var numbers = { 
  numberA: 5,
  numberB: 10,
  sum: function() {
   console.log(this === numbers); // => true
   function calculate() {
    console.log(this === numbers); // => true
    return this.numberA + this.numberB;
   }
   // use .call() method to modify the context
   return calculate.call(this);
  }
};
numbers.sum(); // => 15 

3. Call in new

A variable that references an object actually holds a reference to the object, which means that the variable actually holds a pointer to real data.
The changes to this when using the new keyword actually take the following steps:

Create this = {}.
this may be changed during the execution of new, and then attributes and methods are added;
Returns the changed this.


  function Animal(name) {
    this.name = name
    this.canWalk = true
  }
  var animal = new Animal("beastie")
  alert(animal.name)

Note that if the constructor returns 1 object, then this points to the returned object;


  function Animal() {
    this.name = 'Mousie';
    this.age = '18';
    return {
      name: 'Godzilla'
    } // <-- will be returned
  }

  var animal = new Animal()
  console.log(animal.name) // Godzilla
  console.log(animal.age)//undefined

The important thing to note here is not to forget to use new, otherwise a new function will not be created. Instead, it just executes a function, which is equivalent to a function call. this actually points to window


function Vehicle(type, wheelsCount) { 
 this.type = type;
 this.wheelsCount = wheelsCount;
 return this;
}
// Function invocation
var car = Vehicle('Car', 4); 
car.type;    // => 'Car' 
car.wheelsCount // => 4 
car === window // => true

4. Calling this explicitly, using call and apply

This is the most characteristic place of JavaScript.
The following code:

func.call(obj, arg1, arg2,...)

The first parameter will be used as the reference object of this, and the following parameters will be used as parameters of the function. The solution is to use bind.


function Animal(type, legs) { 
 this.type = type;
 this.legs = legs; 
 this.logInfo = function() {
  console.log(this === myCat); // => true
  console.log('The ' + this.type + ' has ' + this.legs + ' legs');
 };
}
var myCat = new Animal('Cat', 4); 
// logs "The Cat has 4 legs"
setTimeout(myCat.logInfo.bind(myCat), 1000); 
// setTimeout??
 var john = {
  firstName: "John",
  surname: "Smith"
 }
 function func(a, b) {
  alert( this[a] + ' ' + this[b] )
 }
 func.call(john, 'firstName', 'surname') // "John Smith"

As for apply, it only passes in parameters on the side of the array, and the rest is like 1, as follows:


 func.call(john, 'firstName', 'surname')
 func.apply(john, ['firstName', 'surname'])

They can also be used to call the parent constructor in class inheritance in ES5.


  function Runner(name) { 
   console.log(this instanceof Rabbit); // => true
   this.name = name; 
  }
  function Rabbit(name, countLegs) { 
   console.log(this instanceof Rabbit); // => true
   //  Indirect call, calling the parent constructor 
   Runner.call(this, name);
   this.countLegs = countLegs;
  }
  var myRabbit = new Rabbit('White Rabbit', 4); 
  myRabbit; // { name: 'White Rabbit', countLegs: 4 }

5..bind()

Contrast the methods. apply () and. call (), both of which execute functions immediately, whereas the. bind () function returns a new method that binds a pre-specified this and can be called later.

The. bind () method creates a new function that executes in the context of the first argument passed by. bind (), which allows the creation of functions with this preset.


var numbers = { 
 array: [3, 5, 10],
 getNumbers: function() {
  return this.array;  
 }
};
// Create a bound function
var boundGetNumbers = numbers.getNumbers.bind(numbers); 
boundGetNumbers(); // => [3, 5, 10] 
// Extract method from object
var simpleGetNumbers = numbers.getNumbers; 
simpleGetNumbers(); // => undefined or throws an error in strict mode 

When using. bind (), it should be noted that. bind () creates an eternal context chain that cannot be modified. Even if a binding function passes in a different context using. call () or. apply (), it does not change the context to which it was previously connected, and rebinding has no effect.

The binding function can change the context only when the constructor is called, but this is not particularly recommended.

6. The arrow function

The arrow function does not create a context for its own execution, so that this depends on its external function at the time of definition.

The arrow function cannot be changed after binding the context once, even if the context change method is used:


  func()
  function func() {
    alert(this) // [object Window] or [object global] or kind of..
  }
0

This is because the arrow function has a static context and will not be changed by different calls. Therefore, do not use arrow functions to define methods


  func()
  function func() {
    alert(this) // [object Window] or [object global] or kind of..
  }
1

Reference

Four scents of "this"

Gentle explanation of 'this' keyword in JavaScript

The Mystery of JavaScript This

It is highly recommended that students who feel confused read the above three articles, the third of which is the translation of the second. If you have any questions about this, you are also welcome to discuss it, exchange and promote thinking, and make progress together.


Related articles: