According to recent developer surveys, JavaScript is still the most commonly-used programming language in the world and it remains in high demand by various organizations across the globe. In light of that piece of information coupled with the fact that I love writing about JavaScript, I'll be dedicating more time and resources to write up quality content about the language. The topic with which we are now concerned is; Arrow Functions. Without wasting any more time, let's start looking at the ins and outs of Arrow Functions. But before we do that, I'll like us to have a little refresher on regular or traditional JS functions so that it puts everything into perspective. Shall we?
Functions are one of the fundamental building blocks in JavaScript and they are basically blocks of code designed to perform particular tasks. To use a function, you must first define it somewhere in the scope from which you wish to call it.
Functions are defined, or declared, with the function
keyword. Below is the syntax for a function in JavaScript.
function nameOfFunction() {
// Block of code to be executed
}
The declaration begins with the function
keyword, followed by the name of the function. Function declarations load into the execution context before any code runs. This is known as hoisting, meaning you can use the function before you declare it.
Another way of defining a function is by means of a function expression which is basically assigning a function to a variable. Unlike a function declaration, function expressions are not pre-loaded into the execution context, and only run when the program encounters it. Below is an example of a function expression that computes the sum of two numbers.
const sum = function (a, b) {
return a + b
}
Trying to call this function before defining it will result in an error. Now unto arrow functions!
Arrow functions
An arrow function is fundamentally a shorter alternative to traditional JS function expressions with a few semantic differences and a deliberate limitation in their usage. Arrow functions are always anonymous—there is no way to name an arrow function. In the next section, you will explore the syntactical and practical differences between arrow functions and traditional JavaScript functions.
Behavior and Syntax of Arrow Functions
Arrow functions have a few important distinctions in how they work that make them different from traditional JS functions, as well as a few syntactic enhancements. These differences are:
- Arrow functions don't have their own bindings to this
- Arrow Functions Have No
constructor
orprototype
.
Now let us look at what these points mean in detail.
Arrow functions don't have their own bindings to this
: The context inside arrow functions is lexically or statically defined. What this means is that, unlike regular JS functions, the value of this
inside arrow functions is not dependent on how they are called or how they are defined. It depends only on its enclosing context. This might seem a little bit confusing so let's try to understand it by looking at an example. In this example, we will create a basic object and see the use of the object in Javascript
let Student = function(student, age) {
this.student = student;
this.age = age;
this.info = function() {
// logs Student
console.log(this);
setTimeout(function() {
// here this!=Student
console.log(this.student + " is " + this.age +
" years old");
}, 2000);
}
}
let student1 = new Student('James', 19);
// logs : undefined is undefined years old after 3 seconds
student1.info();
The reason that we get undefined
values outputted instead of the proper info is because the function()
defined as the callback for setTimeout() is a traditional JS function and this means that its context is set to the global context or in other words the value of this
is set to the window object.
This happens because every regular JavaScript function defines its own this or context depending on their invocation. The context of the enclosing objects or function does not affect this tendency to automatically define their own context.
Now how do we solve this issue? One obvious solution that might come to mind is, what if the function did not define its own this or context? What if it inherited the context from info()
, because that would mean the callback function gets the this as was defined in info()
Well, that is exactly what arrow functions do. They retain the value of this
from their enclosing context. In the above example, if the callback function in setTimeout() were an arrow function it would inherit the value of this
from its enclosing context – info()
let Student = function(student, age) {
this.student = student;
this.age = age;
this.info = function() {
// logs Student
console.log(this);
setTimeout(() => {
// arrow function to make lexical "this" binding
// here this=Student."this" has been inherited
console.log(this.student + " is " + this.age
+ " years old");
}, 2000);
}
}
let student1 = new Student('James', 19);
// logs : James is 19 years old after 2 seconds
student1.info();
An arrow function’s this
value is the same as the value of this
in its enclosing context that is the context immediately outside it. If used outside any enclosing function, an arrow function inherits the global context, thereby setting the value of this
to the global window object.
Arrow Functions Have No constructor
or prototype
: In JavaScript, functions and classes have a prototype
property, which is what it uses as a blueprint for cloning and inheritance. To demonstrate this, create a function and log the automatically assigned prototype
property to the console:
function myFunction() {
this.number = 8
}
// Log the prototype property of myFunction
console.log(myFunction.prototype)
This will print the following to the console:
Output
{constructor: ƒ}
This shows that in the prototype
property there is an object with a constructor
. This allows you to use the new
keyword to create an instance of the function:
const newInstance = new myFunction()
console.log(newInstance.value)
This will give the value of the number
property that you defined when you first declared the function. In contrast, arrow functions do not have a prototype
property. Create an arrow function and try to log its prototype
to the console, you'll see that the output you get is undefined
. As a result of the missing prototype
property, the new
keyword is not available and you cannot construct an instance of the arrow function.
In addition to these differences that arrow functions exhibit, there are a few optional syntactic changes that make writing arrow functions quicker and less verbose. The next section will show examples of these syntax changes.
Arrow Function Implicit Return
It is common knowledge that the body of a traditional JavaScript function is contained within a block using curly brackets {}
and ends when the code encounters a return
keyword. The following is what this implementation looks like as an arrow function:
const sum = (x, y) => {
return x + y
}
Arrow functions also allow for an omission of the return
keyword and curly brackets altogether. When this happens, there is an implicit return.
const sum = (a, b) => a + b
Implicit return is useful for creating succinct one-line operations in array methods such as map
, filter
and other common array methods. Note that both the brackets and the return
keyword must be omitted. If you cannot write the body as a one-line return statement, then you will have to use the normal block body syntax.
In a case where an object is returned, the syntax requires that you wrap the object literal in parentheses as shown in the example below:
const sum = (a, b) => ({result: a + b})
sum(7, 2)
If you don't enclose the object literal in parentheses, the brackets will be treated as a function body and will not compute a return value.
Note: If your Arrow function has no arguments you should not leave out the parentheses because they are required.
Conclusion
Always try to remember the differences between regular JS functions and Arrow functions. As we have just seen, they might behave differently in the same situations. It is good practice to use arrow functions for callbacks and closures because their syntax is concise and cleaner.
Top comments (3)
Hi Brandon,
I appreciate the effort and style of your post on arrow functions. However, I noticed some fundamental inaccuracies that could be misleading to readers. For example, the statement ‘arrow functions are always anonymous—there is no way to name an arrow function’ is incorrect. Arrow functions can be assigned to variables, effectively giving them a name, like this:
I encourage you to review and correct these points to ensure the information is accurate. Keep up the good work!
Great article, you got my follow, keep writing!
Thank you Al.