Original text: dmitripavlutin.com/when-not-to…
Translator: Front-end wisdom
The more you know, the more you don’t know
Like it and see. Make it a habit
GitHub: github.com/qq449245884… Has included more categories of previous articles, as well as a lot of my documentation and tutorial material. Welcome Star and Perfect, you can refer to the examination points for review in the interview, I hope we can have something together.
In order to ensure readability, this paper adopts free translation rather than literal translation.
Over the years, ES6 has taken JS usability to a new level: arrow functions, classes, etc., which are all great.
The arrow function is one of the most valuable new features, and there are many good articles describing its contextual transparency and short syntax.
But every business has two sides. Often, new features introduce some confusion, one of which is that the arrow function is misleading. This article covers some scenarios in which you should bypass the arrow function in favor of good old function expressions or newer shorthand syntax. And be careful to shorten the code, as this will affect the readability of the code.
1. Define methods on objects
In JS, methods are functions stored in object properties. When the method is called, this points to the object to which the method belongs.
Object literal
Since the arrow function syntax is short, it is attractive to use it to define methods. Let’s give it a try:
const calculate = { array: [1, 2, 3], sum: () => { console.log(this === window); // => true return this.array.reduce((result, item) => result + item); }}; console.log(this === window); // => true // Throws "TypeError: Cannot read property 'reduce' of undefined" calculate.sum();Copy the code
The calculate.sum method is defined with the arrow function. But when called, calculate.sum() raises a TypeError because this.array is undefined.
When the method sum() on the Calculate object is called, the context is still Window. This happens because the arrow function binds the context to the Window object lexical scoped.
Array is the same as window.array, which is undefined.
The solution is to use regular function expressions to define methods. This is determined at call time, not by closed context. Take a look at the fixed version:
const calculate = { array: [1, 2, 3], sum() { console.log(this === calculate); // => true return this.array.reduce((result, item) => result + item); }}; calculate.sum(); / / = > 6Copy the code
Because sum is a regular function, this is the calculate object when calculate.sum() is called. This. array is an array reference, so it counts the elements correctly: 6.
Object prototype
The same rules apply to defining methods on prototype objects. Use an arrow function to define the sayCatName method, with this pointing to the window
function MyCat(name) {
this.catName = name;
}
MyCat.prototype.sayCatName = () => {
console.log(this === window); // => true
return this.catName;
};
const cat = new MyCat('Mew');
cat.sayCatName(); // => undefined
Copy the code
Define function expressions the earlier way:
function MyCat(name) {
this.catName = name;
}
MyCat.prototype.sayCatName = function() {
console.log(this === cat); // => true
return this.catName;
};
const cat = new MyCat('Mew');
cat.sayCatName(); // => 'Mew'
Copy the code
The sayCatName regular function changes the context to a cat object when called as a method: cat.saycatname ().
2. Dynamic context callback function
This is a powerful feature in JS that allows the context to change depending on how the function is called. Usually, the context is the target object where the call takes place, which makes the code more natural, like what happens to that object.
However, the arrow function statically binds the context on the declaration and cannot make it dynamic, but there are good and bad ways to do this, and sometimes we need dynamic binding.
Attaching event listeners to DOM elements is a common task in client programming. The event triggers the handler function with this as the target element, where the arrow function is not flexible.
The following example attempts to use the arrow function for such a handler:
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log(this === window); // => true
this.innerHTML = 'Clicked button';
});
Copy the code
In the global context this refers to the window. When a click event occurs, the browser tries to invoke the handler using the button context, but the arrow function does not change its predefined context. This. InnerHTML is equivalent to window.innerhtml, which has no meaning.
You must apply a function expression that allows you to change this based on the target element:
const button = document.getElementById('myButton');
button.addEventListener('click', function() {
console.log(this === button); // => true
this.innerHTML = 'Clicked button';
});
Copy the code
When the user clicks the button, this in the handler function points to the button. So this question. InnerHTML = ‘Clicked button’ correctly modifies the button text to reflect Clicked state.
3. Call the constructor
This is the newly created object in the construct call. When new MyFunction() is executed, the context of the constructor MyFunction is a new object :this instanceof MyFunction === true.
Note that the arrow function cannot be used as a constructor. JavaScript implicitly prevents this by throwing exceptions.
In any case, this is a setting from the enclosing context, not the newly created object. In other words, the arrow function constructor call is meaningless and ambiguous.
Let’s see what happens if we try to do this:
const Message = (text) => { this.text = text; }; // Throws "TypeError: Message is not a constructor" const helloMessage = new Message('Hello World! ');Copy the code
Execute new Message(‘Hello World! ‘), where Message is an arrow function, JavaScript throws a TypeError, and Message cannot be used as a constructor.
The above example can be fixed using function expressions, which is the correct way to create constructors (including function declarations) :
const Message = function(text) { this.text = text; }; const helloMessage = new Message('Hello World! ');Copy the code
Shorthand syntax
The arrow function has a nice property that lets you omit the argument parentheses (), block braces {}, and return if the function body has only one statement. This helps to write very short functions.
The author’s college programming professor gave his students an interesting assignment: write the shortest function that calculates the length of a string in C, which is a great way to learn and explore a new language.
In real applications, however, many developers read the code. The shortest syntax is not always appropriate to help your colleagues instantly understand the purpose of the method.
At some point, the shorthand function becomes hard to read, so try not to overuse it. Let me show you an example
const multiply = (a, b) => b === undefined ? b => a * b : a * b; const double = multiply(2); double(3); // => 6 multiply(2, 3); / / = > 6Copy the code
Multiply returns the result of the multiplication of two numbers or a closure bound to the first parameter for later multiplication.
This function works fine and looks short. But it was hard to understand what it did in the first place.
To make it more readable, you can restore the optional curly braces and return statements from the arrow function, or use the regular function:
function multiply(a, b) { if (b === undefined) { return function(b) { return a * b; } } return a * b; } const double = multiply(2); double(3); // => 6 multiply(2, 3); / / = > 6Copy the code
It’s good to find a balance between brevity and verbosity to make your code more intuitive.
conclusion
Without a doubt, the arrow function is a good addition. When used correctly, it makes it easy to use.bind() or try to capture context earlier, and it also simplifies code.
An advantage in some cases can be a disadvantage in others. Arrow functions cannot be used when dynamic context is required: define methods, use constructors to create objects, and get targets from this when handling events.
The bugs that may exist after code deployment cannot be known in real time. In order to solve these bugs, I spent a lot of time on log debugging. Incidentally, I recommend a good BUG monitoring tool for youFundebug.
Communication (welcome to join the group, the group will give red envelopes on weekdays, interactive discussion of technology)
Dry goods series of articles summarized as follows, feel good point Star, welcome to add groups to learn from each other.
Github.com/qq449245884…
I am Xiaozhi, the author of the public account “Big Move the world”, and a lover of front-end technology. I will often share what I have learned to see, in the way of progress, mutual encouragement!
Pay attention to the public number, background welfare, you can see the welfare, you know.
P.S. New articles will be sent to the official account one day in advance