Home > Web Front-end > JS Tutorial > 5 ways to correctly handle the this pointer of JS

5 ways to correctly handle the this pointer of JS

青灯夜游
Release: 2019-11-26 15:16:06
forward
2059 people have browsed it

5 ways to correctly handle the this pointer of JS

I like to change the pointer of the function execution context in JS, also known as this pointer.

For example, we can use array methods on array-like objects:

const reduce = Array.prototype.reduce;

function sumArgs() {
  return reduce.call(arguments, (sum, value) => {
    return sum += value;
  });
}

sumArgs(1, 2, 3); // => 6
Copy after login

On the other hand, this is difficult to grasp.

We often find that we use this to point incorrectly. The following teaches you how to simply bind this to the desired value.

[Related course recommendations: JavaScript video tutorial]

Before I start, I need a helper function execute(func) that only executes the function provided as a parameter.

function execute(func) {
  return func();
}

execute(function() { return 10 }); // => 10
Copy after login

Now, moving on to understanding the nature of the error surrounding this: method separation.

1. Method separation problem

Suppose there is a class Person containing fields firstName and lastName. Additionally, it has a method getFullName() which returns the full name of the person. As shown below:

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;

  this.getFullName = function() {
    this === agent; // => true
    return `${this.firstName} ${this.lastName}`;
  }
}

const agent = new Person('前端', '小智');
agent.getFullName(); // => '前端 小智'
Copy after login

You can see that the Person function is called as a constructor: new Person('front end', 'Xiao Zhi'). This inside the function represents the newly created instance.

getfullname() returns the person's full name: 'front-end Xiaozhi'. As expected, this inside the getFullName() method is equal to agent.

What will happen if the auxiliary function executes the agent.getFullName method:

execute(agent.getFullName); // => 'undefined undefined'
Copy after login

The execution result is incorrect: 'undefined undefined', which is a problem caused by incorrect this pointing.

Now in the getFullName() method, the value of this is the global object (window in the browser environment). This is equal to window, and the execution result of ${window.firstName} ${window.lastName} is 'undefined undefined'.

This happens because the method is detached from the object when execute(agent.getFullName) is called. Basically what happens is just a regular function call (not a method call):

execute(agent.getFullName); // => 'undefined undefined'

// 等价于:

const getFullNameSeparated = agent.getFullName;
execute(getFullNameSeparated); // => 'undefined undefined'
Copy after login

This is what is called a method being detached from its object. When a method is detached and then executed, this has no connection to the original object.

1. In order to ensure that this inside the method points to the correct object, this must be done

2. Execute the method in the form of a property accessor: agent.getFullName() or bind this statically Determine the contained object (using arrow function, .bind() method, etc.)

Method separation problem, and the resulting incorrect this pointing, generally occur in the following situations:

Callback

// `methodHandler()`中的`this`是全局对象
setTimeout(object.handlerMethod, 1000);
Copy after login

When setting up the event handler

// React: `methodHandler()`中的`this`是全局对象
<button onClick={object.handlerMethod}>
  Click me
</button>
Copy after login

Then we introduce some useful methods, that is, if the method is separated from the object, How to make this point to the desired object.

2. Close the context

The simplest way to keep this pointing to the class instance is to use an additional variable self:

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
  const self = this;
  this.getFullName = function() {
    self === agent; // => true
    return `${self.firstName} ${self.lastName}`;
  }
}
const agent = new Person(&#39;前端&#39;, &#39;小智&#39;);
agent.getFullName();        // => &#39;前端 小智&#39;
execute(agent.getFullName); // => &#39;前端 小智&#39;
Copy after login

getFullName() statically closes the self variable, effectively manually binding this.

Now, when calling execute(agent.getFullName), everything works fine because this inside the getFullName() method always points to the correct value.

3. Using arrow functions

Is there a way to statically bind this without additional variables? Yes, that's exactly what arrow functions do.

Use arrow functions to reconstruct Person:

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;

  this.getFullName = () => `${this.firstName} ${this.lastName}`;
}

const agent = new Person(&#39;前端&#39;, &#39;小智&#39;);

agent.getFullName();        // => &#39;前端 小智&#39;
execute(agent.getFullName); // => &#39;前端 小智&#39;
Copy after login

Arrow functions bind this lexically. Simply put, it uses the value from the external function this where it is defined.

It is recommended to use arrow functions in all cases where an external function context is required.

4. Binding context

Now let’s go one step further and reconstruct Person using classes in ES6.

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  getFullName() {
    return `${this.firstName} ${this.lastName}`;
  }
}

const agent = new Person('前端', '小智');

agent.getFullName();        // => '前端 小智'
execute(agent.getFullName); // => &#39;undefined undefined&#39;
Copy after login

Unfortunately, even with the new class syntax, execute(agent.getFullName) still returns "undefined undefined".

In the case of classes, using an additional variable self or an arrow function to fix the pointing of this will not work.

But there is a trick involving the bind() method, which binds the context of the method into the constructor:

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;

    this.getFullName = this.getFullName.bind(this);
  }

  getFullName() {
    return `${this.firstName} ${this.lastName}`;
  }
}

const agent = new Person(&#39;前端&#39;, &#39;小智&#39;);

agent.getFullName();        // => &#39;前端 小智&#39;
execute(agent.getFullName); // => &#39;前端 小智&#39;
Copy after login

this.getFullName in the constructor = this.getFullName.bind( this) binds the method getFullName() to the class instance.

execute(agent.getFullName) works as expected, returning 'frontend Xiaozhi'.

5. Fat arrow method

bind method is a bit too lengthy, we can use the fat arrow method:

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  getFullName = () => {
    return `${this.firstName} ${this.lastName}`;
  }
}

const agent = new Person(&#39;前端&#39;, &#39;小智&#39;);

agent.getFullName();        // => &#39;前端 小智&#39;
execute(agent.getFullName); // => &#39;前端 小智&#39;
Copy after login

Fat arrow method getFullName =() =>{…} is bound to a class instance, even if the method is detached from its object.

This method is the most effective and concise way to bind this in a class.

6. Summary

Methods separated from the object will cause the problem of incorrect this pointing. To bind this statically, you can manually use an additional variable self to hold the correct context object. However, a better alternative is to use arrow functions, which essentially serve to lexically bind this.

In a class, you can use the bind() method to manually bind the class method in the constructor. Of course, if you don't need to use the lengthy method of bind, you can also use the concise and convenient fat arrow representation method.

Original text: https://github.com/valentinogagliardi/Little-JavaScript-Book/blob/v1.0.0/manuscript/chapter5.md

This article comes from js tutorial Column, welcome to learn!

The above is the detailed content of 5 ways to correctly handle the this pointer of JS. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:fundebug.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template