Introduction to JS Arrow Function
Arrow functions, also known as "fat arrow" functions, are a new feature in JavaScript that provides a concise and more expressive syntax for defining functions. They were introduced in ECMAScript 6 (also known as ECMAScript 2015) and have become widely used in modern JavaScript code.
They have become a popular feature of the language, thanks to their concise syntax and ability to capture the lexical this
value. Arrow functions are often used in place of traditional function expressions and can make your code more readable and easier to write.
Instead of the function keyword, it uses an arrow (=>
) made up of an equal sign and a greater-than character (not to be confused with the greater-than-or-equal operator, which is written >=
).
const power = (base, exponent) => {
let result = 1;
for (let count = 0; count < exponent; count++) {
result *= base;
}
return result;
};
The arrow comes after the list of parameters and is followed by the function’s body. It expresses something like “this input (the parameters) produces this result (the body).”
In this article, we will explore the syntax and usage of JavaScript arrow functions, including how to use them as methods, constructors, and in combination with other functions.
Using arrow functions
Functions allow us to reduce repetition, and different approaches including the arrow function style make it easy to achieve this. As stated, arrow functions allow us to be concise in a simple manner. Let’s start with some examples to show how we can use arrow functions in JavaScript.
Taking a single argument
Here is an example of an arrow function that takes a single argument x
and returns its square:
let square = x => x * x;
console.log(square(3));
Output
9
As you can see, the arrow function has a shorter syntax than a regular function. It does not have a function
keyword, a pair of curly braces, or a return
statement. Instead, it consists of the =>
operator (which is sometimes referred to as the "fat arrow"), followed by the function body.
Taking multiple arguments
If the function takes more than one argument, you need to enclose the arguments in parentheses. Let’s illustrate this by creating an arrow function that adds two numbers parsed to it.
let add = (x, y) => x + y;
console.log(add(3, 4));
Output
7
Structuring arrow functions
Depending on the structure of the code block, arrow functions can be shaped in different ways e.g. no curly braces, etc.
If the function body is a single expression, you can omit the curly braces and the return
statement. The value of the expression will be returned automatically. Let’s show this by creating an arrow function that squares a number.
let square = (x) => x * x;
console.log(square(3));
Output
9
The above code is equivalent to let square = x => { return x * x; }
.
If the function body consists of multiple statements, you need to enclose them in curly braces and use a return
statement to specify the value to be returned.
To showcase this scenario, we will create an arrow function that adds and multiplies its argument and returns an array.
let addAndMultiply = (x, y) => {
let sum = x + y;
let product = x * y;
return [sum, product];
};
console.log(addAndMultiply(3, 4));
Output
[ 7, 12 ]
Lexical this
in arrow functions
An important aspect of arrow functions is that they behave differently from normal functions. The difference is subtle but important. Arrow functions do not have their own value of this
. The value of this
in an arrow function is inherited from the enclosing (lexical) scope.
Functions have a special variable this
that refers to the object via which the method was invoked. As the value of this
is dynamically given based on the function invocation, it is sometimes called dynamic this
. A function is executed in two scopes-lexical and dynamic. A lexical scope is a scope that surrounds the function scope, and the dynamic scope is the scope that called the function (usually an object).
Consider this example:
var greeter = {
default: "Hello ",
greet: function (names){
names.forEach(function(name) {
console.log(this.default + name); //Cannot read property
'default' of undefined
})
}
}
console.log(greeter.greet(['hello', 'world']))
We are passing a subroutine to the forEach()
function on the names
array. This subroutine has an undefined value of this
, and unfortunately, it does not have access to this of the outer method greet
. Clearly, this subroutine needs a lexical this
,derive this
from the surrounding scope of the greet
method. Traditionally, to fix this limitation, we assign the lexical this
into a variable, which is then accessible to the subroutine via closure.
We can fix the earlier example as follows:
var greeter = {
default: "Hello ",
greet: function (names){
let that = this
names.forEach(function(name) {
console.log(that.default + name);
})
}
}
console.log(greeter.greet(['hello', 'world']))
This is a reasonable hack to simulate lexical this
. However, the problem with such hacks is that it creates too much noise for the person writing or reviewing this
code. First, you have to understand the quirk of the behavior of this
. Even if you understand this
behavior well, you will need to continuously remain on the lookout for such hacks in your code.
Arrow functions have lexical this
and do not require such a hack. They are more suited as subroutines because of this
. We can covert the preceding example to use lexical this
using the arrow function:
var greeter = {
default: "Hello ",
greet: function (names){
names.forEach(name=> {
console.log(this.default + name); //lexical 'this'
available for this subroutine
})
}
}
console.log(greeter.greet(['hello', 'world']))
Benefits of arrow function
Arrow functions have a number of benefits and features that make them useful in various situations. Here are a few examples:
- Arrow functions do not have their own
this
value. They inherit thethis
value of the surrounding scope. This makes them particularly useful in object-oriented programming, wherethis
often refers to the object that the function belongs to. - Arrow functions do not have a
arguments
object. If you need to access the arguments passed to a function, you can use the rest operator (...
) instead. - Arrow functions can be used as callbacks and passed as arguments to other functions. For example, you can use them with array methods such as
map()
,filter()
, andreduce()
.
Here is an example that uses an arrow function as a callback in the map()
method
let numbers = [1, 2, 3, 4, 5];
let squares = numbers.map((x) => x * x);
console.log(squares); // [1, 4, 9, 16, 25]
Output
[ 1, 4, 9, 16, 25 ]
This approach makes it a lot easier to read.
Summary
In conclusion, arrow functions are a concise and powerful feature of JavaScript that allow developers to write shorter and more expressive code. They are particularly useful when working with higher-order functions and in scenarios where the this keyword might be confusing. However, it's important to keep in mind that arrow functions behave differently than traditional functions in terms of their lexical this binding and cannot be used as constructors. Overall, arrow functions are a useful tool in any JavaScript developer's toolkit and can greatly improve the readability and maintainability of your code.
References
Arrow function expressions - JavaScript | MDN (mozilla.org)