Author: Dmitri Pavlutin
Translator: Crazy Technology House
Original text: https://dmitripavlutin.com/fi...
Reproduction is strictly prohibited without permission
I like the feature in JavaScript that can change the execution context of a function (also known as this).
For example, you can use array methods on objects like arrays:
const reduce = Array.prototype.reduce; function sumArgs() { return reduce.call(arguments, (sum, value) => { return sum += value; }); } sumArgs(1, 2, 3); // => 6
But on the other hand, this keyword is difficult to grasp.
You may often check the reason why this value is incorrect. The following sections will teach you some simple ways to bind this to the required values.
Before I start, I need an auxiliary function execute(func). It is only used to perform functions as parameters:
function execute(func) { return func(); } execute(function() { return 10 }); // => 10
Now let's continue to understand the nature of the error around this: method separation.
1. Method separation
The Person class contains fields firstName and lastName. In addition, it also has the getFullName() method, which returns the full name.
One possible implementation of Person is:
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('John', 'Smith'); agent.getFullName(); // => 'John Smith'
You'll see the person function called as a constructor: new Person('John ',' Smith '). Create a new instance inside the Person function.
agent.getFullName() returns the full name of person:'John Smith'. As expected, this in the getFullName() method is equivalent to agent.
What happens if the help function executes the help.getFullName method:
execute(agent.getFullName); // => 'undefined undefined'
The execution result is incorrect:'undefined undefined'. This problem is caused by the incorrect value of this.
Now, in the method getFullName(), the value of this is the global object (window in the browser environment). Assuming this equals window, the evaluation of ${window.firstName} ${window.lastName} is undefined undefined.
This happens because the method is separated from the object when execute(agent.getFullName) is called. Basically, it just happens on regular function calls (not method calls):
execute(agent.getFullName); // => 'undefined undefined' // is equivalent to: const getFullNameSeparated = agent.getFullName; execute(getFullNameSeparated); // => 'undefined undefined'
This effect is what I call the separation of objects. When a method is separated and subsequently executed, it has nothing to do with its original object.
To ensure that this in the method points to the correct object, you must:
- Execute this method as a property accessor: agent.getFullName()
- Or statically bind this to the included object (using arrow functions, the. bind() method, etc.)
In the method separation problem, the return this is incorrect, which appears in the following different forms:
When setting callback
// `this` inside `methodHandler()` is the global object setTimeout(object.handlerMethod, 1000);
When setting up event handlers
// React: `this` inside `methodHandler()` is the global object <button onClick={object.handlerMethod}> Click me </button>
Let's continue to understand some useful ways to solve the problem of keeping the method pointing to the desired object even if it is separate from the object.
2. Close the context
The easiest way to point this to a class instance is to use the 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('John', 'Smith'); agent.getFullName(); // => 'John Smith' execute(agent.getFullName); // => 'John Smith'
getFullName() closes the self variable statically, effectively binding to this manually.
Now, when you call execute(agent.getFullName), you return'John Smith', because the getFullName() method always has the correct this value, so it works.
3. Semanticize this using arrow function
Is there a way to statically bind this without any other variables? Yes, that's what the arrow function does.
To use the arrow function, let's reconstruct Person:
function Person(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; this.getFullName = () => `${this.firstName} ${this.lastName}`; } const agent = new Person('John', 'Smith'); agent.getFullName(); // => 'John Smith' execute(agent.getFullName); // => 'John Smith'
The arrow function binds this lexically. In short, it uses this value of the external function defined therein.
I recommend using arrow functions in all cases where external function contexts are required.
4. Binding context
Let's take another step forward and use the ES2015 class to reconstruct Person.
class Person { constructor(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; } getFullName() { return `${this.firstName} ${this.lastName}`; } } const agent = new Person('John', 'Smith'); agent.getFullName(); // => 'John Smith' execute(agent.getFullName); // => 'undefined undefined'
Unfortunately, even with the new class syntax, execute(agent.getFullName) still returns'undefined undefined'.
In the case of classes, you cannot use additional variable self or arrow functions to fix the value of this.
But there's one involved. bind() The technique of the method, which binds the context of the method to 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('John', 'Smith'); agent.getFullName(); // => 'John Smith' execute(agent.getFullName); // => 'John Smith'
this.getFullName = this.getFullName.bind(this) in the constructor binds the getFullName() method to a class instance.
Excute (agent. getFullName) works properly and returns to'John Smith'.
5. Fat Arrow Method
The above method of using manual context binding requires boilerplate code. Fortunately, there is still room for improvement.
You can use JavaScript Class field recommendation To define 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('John', 'Smith'); agent.getFullName(); // => 'John Smith' execute(agent.getFullName); // => 'John Smith'
The fat arrow function getFullName =() => {...} is bound to the class instance, even if you separate the method from its object.
This is the most efficient and concise way to bind this in a class.
Six. Conclusion
There are many misunderstandings about this by separating it from objects. You should be aware of the impact.
To statically bind this, you can manually use an additional variable self to save the correct context object. But the better choice is to use the arrow function, which is naturally designed to bind this lexically.
In classes, you can use the bind() method to manually bind class methods inside the constructor.
If you want to skip writing boilerplate code, the new JavaScript suggested class fields bring fat arrow methods that automatically bind this to class instances.
Wechat Public Number: Front-end Pioneer
Welcome to scan the two-dimensional code, pay attention to the public number, and push you fresh front-end technical articles every day.
Welcome to other great articles in this column:
- Deep Understanding of Shadow DOM v1
- Step by step teach you how to use WebVR to implement virtual reality games
- Thirteen Modern CSS Frameworks to Improve your Development Efficiency
- Fast Start Bootstrap Vue
- How does the JavaScript engine work? Everything you need to know from call stack to Promise
- WebSocket Reality: Real-time Communication between Node and React
- 20 interview questions about Git
- In-depth analysis of Node.js console.log
- What exactly is Node.js?
- Build an API server with Node.js in 30 minutes
- Object Copy of Javascript
- Programmers earn less than 30K a month before they are 30 years old. Where to go?
- 14 Best JavaScript Data Visualization Libraries
- Eight top-level VS Code extension plug-ins for the front end
- Complete guide to Node.js multithreading
- Four Solutions and Implementation of Converting HTML to PDF