One of the most anticipated features in modern JS is the arrow function, identified by =>. The arrow function has two main advantages: a very concise syntax, and an intuitive scope-this binding.
Because of these advantages, arrow functions are more popular than other forms of function declarations. For example, the popular Airbnb ESLint Configuration library forces anonymous functions to be created using JavaScript arrow functions.
However, like everything else in the world, arrow functions have some advantages and some “disadvantages”, which requires some trade-offs.
Learning how to balance is the key to using arrow functions well. In this article we will review how arrow functions work, and then delve into how arrow functions improve our code in real code, as well as some cases where arrow functions are not recommended.
What is an arrow function
Arrow functions in JS are roughly the same as lambda in Python (the keyword that defines anonymous functions in Python) and blocks in Ruby (similar to closures). These anonymous functions have their own special syntax: they first take a certain number of arguments and then execute in the scope or nearest scope of the function that defines them.
We’ll explore these in more detail next.
Syntax for arrow functions
The arrow function has a general structure, but there are many special cases that can be simplified. The core structure is as follows:
(argument1, argument2, ... argumentN) => {
// function body
}
Copy the code
Inside the parentheses are a series of arguments, followed by an arrow symbol =>, and finally the function body. This is very much like a traditional function, except that we omit the function keyword and add a => after the argument.
Also, there are a number of cases where the structure of the arrow function becomes more concise.
First, if the function body contains a single expression, you can write the expression on one line without the curly braces, and the result of the expression will be returned by the function. Such as:
const add = (a, b) = > a + b;
Copy the code
Second, if this is passed in as a single argument, you can omit the parentheses in the argument section. Such as:
const getFirst = array= > array[0];
Copy the code
As you can see, this is much cleaner, and we’ll explain more features later.
Advanced grammar
It will be very helpful if you know this advanced grammar.
First, if you try to write a function on a line and the value returned is the contents of an object, you might want to write something like this:
(name, description) => {name: name, description: description};
Copy the code
The problem with this syntax is that it can be confusing, you might think you’re writing the body of a function. If you want to return a single object, wrap the object in parentheses:
(name, description) => ({name: name, description: description});
Copy the code
Closed context scope
Unlike other forms of functions, arrow functions do not have their own execution context. In practice, this means that both this and arguments in the code inherit from their parent functions.
For example, compare the following arrow function with a traditional function:
const test = {
name: 'test object'.createAnonFunction: function() {
return function() {
console.log(this.name);
console.log(arguments);
};
},
createArrowFunction: function() {
return (a)= > {
console.log(this.name);
console.log(arguments); }; }};Copy the code
We have an object with two methods, each of which returns an anonymous function. The difference is that the first method uses the traditional function expression, and the second method uses the arrow function expression. If we run with the same parameters passed in, we get two different results.
const anon = test.createAnonFunction('hello'.'world');
// Returns an anonymous function
const arrow = test.createArrowFunction('hello'.'world');
anon();
//undefined
/ / {}
// this->window
arrow();
//test object
//object { '0': 'hello', '1': 'world' }
//this->test
Copy the code
The first anonymous function has its own context (referring to a non-test object), no reference to the this.name attribute when you call it (note: this now refers to window), and no arguments to call when it was created. Another, the arrow function has the same context as the function that created it, giving it access to arguments and objects.
The arrow function improves your code
One of the main use cases for traditional lambda functions, which is now implemented in JavaScript arrow functions, is the function for traversing arrays. For example, if you have an array with values and you want to map through each item, the arrow function is recommended:
const words = ['hello'.'WORLD'.'Whatever'];
const downcasedWords = words.map(word= > word.toLowerCase());
Copy the code
An extremely common example is to return a value of an object:
const names = objects.map(object= > object.name);
Copy the code
Similarly, when replacing the traditional for loop with forEach, the arrow function actually intuitively keeps this from the parent level
this.examples.forEach(example= > {
this.runExample(example);
});
Copy the code
Promise and Promise chain
Arrow functions also make code more intuitive and concise when writing asynchronous programs.
Promise makes it easier to write asynchronous programs. While you are happy to use async/await, you need to understand promises as they are based on.
With Promise, you still need to define the callback function when your code completes execution. This is an ideal place for arrow functions, especially if you are generating a function that is stateful and you want to reference something in an object.
this.doSomethingAsync().then((result) = > {
this.storeResult(result);
});
Copy the code
Object conversion
Another common and very useful use of arrow functions is object conversion for encapsulation. In vue.js, for example, there is a common pattern to include parts of the Vuex store directly into the Vue component using mapState. This involves defining a set of Mappers that are used to go from the original object to the full transformation output, which is really necessary in component problems. For this simple series of transformations, the arrow function is the most appropriate. Such as:
export default {
computed: {
...mapState({
results: state= > state.results,
users: state= >state.users, }); }}Copy the code
You should not use arrow function scenarios
There are many scenarios where the arrow function is not recommended, in which case it is not only unhelpful, but also causes unnecessary trouble.
The first is the method in the object. Here is an example of a function context to help us understand. There was a trend to use the syntax of the class class and the arrow function to automatically bind methods to it. For example, event methods can be used, but are still bound to the class class. It looks like the following example:
class Counter {
counter = 0;
handleClick = (a)= > {
this.counter++; }}Copy the code
In this approach, if it is called by a click event function, it can still access the instance data even though it is not in the context of Counter, which has obvious drawbacks.
It does provide a shortcut to bind functions in this way, but the expression of functions is varied and rather unintuitive. If you try to use this object in a prototype, it’s not good for testing and can cause problems. Instead, it is recommended to use a regular binding, if necessary, in the instance constructor:
class Counter {
counter = 0;
handleClick() {
this.counter++;
}
constructor() {
this.handleClick = this.handleClick.bind(this); }}Copy the code
Deep calls
Another problem with using arrow functions is that you use a lot of combination calls, especially deep calls. The simple reason is that, like anonymous functions, tracing the stack is complicated.
This is not a problem if your function is just one layer below, rather than iterating deep. But if you define functions as arrow functions and call back and forth between them, you will get confused by the code when debugging bugs, and even get error messages like this:
{anonymous}()
{anonymous}()
{anonymous}()
{anonymous}()
{anonymous}()
/ / anonymous anonymous
Copy the code
A function with a dynamic context
One of the most confusing situations for arrow functions is when this is dynamically bound. If you use the arrow function in any of the following situations, the dynamic binding of this will not work as expected, and you will be confused as to why the code does not work as expected, and will cause problems for the people you work with later. Some typical examples:
- Event, this points to the current target property
- In jquery, most of the time this refers to the currently selected element
- In the vue,
methods
andcomputed
In thethis
Points to components of vUE.
Of course, you can use the arrow function sparingly in the above situations. But especially in the case of jquery and Vue, this often interferes with normal functionality and leaves you confused as to why code that looks like someone else’s code just doesn’t work.
conclusion
The arrow function is a very special property of the JS language and makes the code more unpredictable in many cases. However, like other language features, they have their pros and cons. So we should use it only as a tool, not mindlessly simply replace all arrow functions.
This article is just a translation of personal interest, if there are mistakes still hope to be corrected. The copyright of the article belongs to the author of the original article.
Codeburst.io /javascript-…