Eight(8) ES6(ECMAScript 6) features that you need to know before learning React
Table of contents
Introduction
ES6, also known as ECMAScript 2015, is the sixth version of the ECMAScript language specification. ECMAScript is a standardized scripting language specification on which many programming languages, including JavaScript, are based. ES6 was a major update to the language, introducing several new features and syntax enhancements that made JavaScript more powerful, flexible, and easier to use.
ES6 Features
React is built using JavaScript, and many of the features used in React are features of ES6. Here are some ES6 features that are particularly useful to know before learning React:
1. let and const
ES6 introduced two new ways to declare variables in JavaScript: let and const. These are block-scoped variables that have a different scope than variables declared with var.
The main difference between const and let is that const variables are read-only, meaning their value cannot be reassigned once they are declared, whereas let variables can be reassigned.
Here is an example to illustrate the difference between const and let:
let favoriteColor = “blue”;
const age = 25;
console.log(favoriteColor); // Output: "blue"
console.log(age); // Output: 25
favoriteColor = "green";
// age = 26; // This will result in an error because age is a constant and its value cannot be reassigned.
console.log(favoriteColor); // Output: "green"
console.log(age); // Output: 25
In this example, we declare a variable called favoriteColor using let and initialize it to “blue”. We also declare a constant called age and initialize it to 25.
We then log the initial values of favoriteColor and age to the console, which outputs “blue” and 25, respectively.
Next, we reassign the value of favoriteColor to “green”, which is allowed because favoriteColor was declared using let. However, we try to reassign the value of age to 26, which would result in an error because age was declared using const and its value cannot be reassigned.
2. Arrow functions
Arrow functions are a new syntax for writing JavaScript functions introduced in ECMAScript 6 (ES6). Arrow functions provide a more concise syntax for defining functions compared to traditional function expressions. They are commonly used in modern JavaScript development, particularly in functional programming and in writing code that uses callbacks, event listeners, and promises.
Here is an example of an arrow function that returns the sum of two numbers:
const add = (a, b) => {
return a + b;
};
console.log(add(2, 3)); // Output: 5
Arrow functions also have some unique features, such as:
2. 1. Lexical this binding: Arrow functions do not bind their own ‘this’ value, but instead inherit ‘this’ from the surrounding context in which they were defined. This can be useful in certain situations, such as when working with event listeners or in functional programming.
Here is an example that demonstrates the use of the ‘this ’keyword in an arrow function:
const myObject = {
value: 10,
getValue: function() {
const innerFunc = () => {
console.log(this.value);
};
innerFunc();
}
};
myObject.getValue(); // Output: 10
In this example, we have an object myObject with a property value set to 10, and a method getValue that defines an inner arrow function innerFunc. When getValue is called, it invokes innerFunc which logs the value of this.value to the console.
Because innerFunc is an arrow function, it inherits this from the surrounding context in which it was defined — in this case, the getValue method of myObject. As a result, this.value refers to the value property of myObject, which has a value of 10.
If we had used a traditional function expression instead of an arrow function for innerFunc, this keyword inside the function would have referred to the global window object (in a browser environment), or the undefined value (in strict mode). This is because this keyword in a traditional function expression is determined at the time the function is called, not at the time it is defined.
2. 2. Implicit return: If the function body contains only one statement, and that statement is an expression, the return keyword and curly braces can be omitted. The expression will be implicitly returned.
Here is an example of an arrow function with an implicit return:
const double = x => x * 2;
console.log(double(5)); // Output: 10
- Template literals
Template literals are a feature of the JavaScript programming language that allows for the creation of strings that can contain expressions and variables in a concise and readable manner. They are denoted by backticks (`) instead of the traditional single quotes (‘) or double quotes (“).
Template literals allow for the embedding of expressions in the string using the ${} syntax, which allows for the evaluation of JavaScript expressions within the string. This means that the values of variables or the results of expressions can be inserted directly into the string without having to concatenate them using the + operator.
For example:
const name = ‘John’;
const age = 30;
console.log(`My name is ${name} and I am ${age} years old.`);
This will output:
My name is John and I am 30 years old.
Template literals can also span multiple lines, making it easier to write multi-line strings without having to use escape characters.
- Destructuring
Destructuring allows you to extract values from arrays or properties from objects and assign them to distinct variables using a syntax that resembles the structure of the data being extracted. It provides a concise way to extract only the required values from data structures, making the code more readable and easier to understand.
In array destructuring, you can extract values from an array and assign them to variables by enclosing the variables in square brackets and using the array name on the right-hand side of the equals sign.
Here’s an example of destructuring an array:
// Create an array of numbers
const numbers = [1, 2, 3, 4, 5];
// Destructure the first and last elements of the array
const [first, , , , last] = numbers;
// Output the first and last elements
console.log(`The first element is ${first}`); // Output: The first element is 1
console.log(`The last element is ${last}`); // Output: The last element is 5
In object destructuring, you can extract values from an object and assign them to variables by enclosing the variables in curly braces and using the object name on the right-hand side of the equals sign.
Here’s an example of destructuring an object:
// Create an object representing a person
const person = {
firstName: 'John',
lastName: 'Doe',
age: 30,
address: {
street: '123 Main St',
city: 'Anytown',
state: 'CA',
zip: '12345'
}
};
// Destructure the person object to get the firstName, lastName, and city properties
const { firstName, lastName, address: { city } } = person;
// Output the values of the firstName, lastName, and city variables
console.log(`My name is ${firstName} ${lastName}, and I live in ${city}.`); // Output: My name is John Doe, and I live in Anytown.
- Spread syntax
The spread syntax allows you to spread the elements of an array or an object into another array or object. This can make it easier to merge arrays or objects or to pass arguments to a function. It is denoted by the ellipsis (…) operator and can be used in a variety of contexts to provide a concise and powerful way of working with data.
When used with arrays, the spread syntax can be used to create a new array that combines the elements of multiple arrays. For example:
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = […arr1, …arr2];
console.log(combined); // [1, 2, 3, 4, 5, 6]
The spread syntax can also be used to copy an array, which is useful when you need to modify an array without affecting the original. For example:
const original = [1, 2, 3];
const copy = […original];
copy.push(4);
console.log(original); // [1, 2, 3]
console.log(copy); // [1, 2, 3, 4]
When used with objects, the spread syntax can be used to create a new object that combines the properties of multiple objects. For example:
const obj1 = { foo: 'bar' };
const obj2 = { baz: 'qux' };
const combined = { …obj1, …obj2 };
console.log(combined); // { foo: 'bar', baz: 'qux' }
The spread syntax can also be used to add new properties to an object or overwrite existing properties. For example:
const original = { foo: 'bar' };
const copy = { …original, baz: 'qux' };
console.log(original); // { foo: 'bar' }
console.log(copy); // { foo: 'bar', baz: 'qux' }
- Promises
A promise is an object representing the eventual completion or failure of an asynchronous operation. It is a way of handling asynchronous code that allows developers to write code that is more readable and easier to reason about.
A promise has three states: pending, fulfilled, or rejected.
When a promise is pending, it means that the asynchronous operation has not yet been completed.
When a promise is fulfilled, it means that the operation has been completed successfully, and the promise returns a value.
When a promise is rejected, it means that the operation has failed, and the promise returns a reason for the failure.
Here’s an example of using promises:
function addNumbers(a, b) {
return new Promise((resolve, reject) => {
if (typeof a !== 'number' || typeof b !== 'number') {
reject('Both arguments must be numbers.');
} else {
const sum = a + b;
resolve(sum);
}
});
}
addNumbers(2, 3)
.then(sum => {
console.log(`The sum of 2 and 3 is ${sum}.`);
})
.catch(error => {
console.error(error);
});
In this example, we define a function called addNumbers() that takes two arguments and returns a promise. Inside the promise, we check if both arguments are numbers and if so, we calculate their sum and resolve the promise with the result. If either argument is not a number, we reject the promise with an error message.
We then call addNumbers() with arguments of 2 and 3, and chain a then() method to log the resulting sum to the console when the promise is fulfilled. If there is an error, the catch() method is called and the error message is logged to the console.
7. Modules
Modules are a standardized way of organizing and sharing code between files and projects. A module is a self-contained block of code that can define variables, functions, classes, and other objects, and can selectively expose these objects to other parts of the application.
Modules allow you to import and export code between different files and namespaces. You can now export a default value from a module using the default keyword, or export multiple named values using the export keyword.
Here are examples of both default exports and named exports
Default export example:
// math.js
const add = (a, b) => a + b;
export default add;
In this example, we define a function called add and export it as the default export of the math.js module using the export default syntax.
We can then import the add function in another module like this:
// app.js
import add from './math.js';
console.log(add(2, 3)); // logs 5
In this importing module, we use the import statement to import the default export from the math.js module. Since we used export default, we can assign the imported object to any variable name we want, in this case, add.
Named export example:
// utils.js
export const formatName = (firstName, lastName) => `${lastName}, ${firstName}`;
export const formatNumber = (num) => {
const formatted = num.toLocaleString('en-US', { style: 'decimal' });
return `$${formatted}`;
};
In this example, we define two functions called formatName and formatNumber and export them as named exports of the utils.js module using the export syntax.
We can then import the named exports in another module like this:
// app.js
import { formatName, formatNumber } from './utils.js';
console.log(formatName('John', 'Doe')); // logs "Doe, John"
console.log(formatNumber(123456.78)); // logs "$123,456.78"
In this importing module, we use the import statement to import the named exports from the utils.js module. We use curly braces {} to specify which exports we want to import, and we use the same names that we used in the exporting module (formatName and formatNumber in this case).
8. Default parameters
Default parameters allow for the specification of default values for function parameters. If a function is called with a missing or undefined value for a parameter with a default value, the default value is used instead.
Default parameters are denoted by assigning a default value to a function parameter in the function definition. For example:
function greet(name = 'World') {
console.log(`Hello, ${name}!`);
}
greet(); // outputs "Hello, World!"
greet('John'); // outputs "Hello, John!"
Default parameters can be used with any type of function parameter, including object and array destructuring. For example:
function processUser({ name = 'Unknown', age = 0 }) {
console.log(`The user ${name} is ${age} years old.`);
}
processUser({}); // outputs "The user Unknown is 0 years old."
processUser({ name: 'John', age: 30 }); // outputs "The user John is 30 years old."
Conclusion
In conclusion, understanding ES6 features is essential to effectively work with React. The let and const, arrow functions, template literals, destructuring, spread syntax, promises, modules, and default parameters, are some of the most commonly used features in React development. Learning these features will not only make your code more concise and readable but also increase your productivity by providing a more modern syntax for creating robust and scalable React applications. As React continues to evolve, it’s important to keep up with the latest ES6 features to stay up-to-date with the best practices in the industry.
Bonus
1. Async/await
Async/await is a feature in modern JavaScript that simplifies the process of working with asynchronous code. It was introduced in ECMAScript 2017 (ES8) and is built on top of promises, another feature that was added in ES6.
In traditional asynchronous programming, callbacks or promises are used to handle the response from an asynchronous operation. However, this can lead to callback hell or complicated chains of promises. Async/await provides a simpler, more readable syntax for working with asynchronous code.
Here’s how async/await works:
The async keyword is used to define a function that returns a promise. The async keyword essentially turns the function into a generator function that yields promises.
The await keyword is used to pause the execution of the async function until a promise is fulfilled. When an await expression is encountered, the function is paused and control is returned to the event loop, allowing other code to execute. When the promise is fulfilled, the function resumes execution and the result of the promise is returned.
Here’s an example of an async function that uses await to handle a network request:
async function fetchUserData(userId) {
const response = await fetch(`https://api.example.com/users/${userId}`);
const userData = await response.json();
return userData;
}
In this example, the fetchUserData function is defined as an async function using the async keyword. Inside the function, we use the await keyword to pause the execution of the function until the fetch call completes and returns a response object. We then use await again to pause the execution of the function until the response has been converted to JSON using the json() method. Finally, we return the userData object.
Using async/await, we can write asynchronous code that looks and behaves like synchronous code, making it easier to read and understand.
2. Filter and map
Filter and map are two common higher-order functions, they are features of ECMAScript 5 (ES5) that are used to manipulate arrays.
Filter is used to create a new array with all elements that pass a certain test (predicate) provided as a callback function. The filter method does not modify the original array and returns a new array with the filtered elements.
Here’s an example of using filter to create a new array of even numbers from an array of integers:
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const evenNumbers = numbers.filter(number => number % 2 === 0);
console.log(evenNumbers); // [2, 4, 6, 8, 10]
In this example, we define an array of numbers and use the filter method to create a new array evenNumbers that contains only the even numbers from the original array. We pass a callback function to filter that tests each element in the array to see if it is even, and returns true or false accordingly.
Map, on the other hand, is used to create a new array by applying a transformation function to each element in the original array. The map method does not modify the original array and returns a new array with the transformed elements.
Here’s an example of using map to create a new array of squared numbers from an array of integers:
const numbers = [1, 2, 3, 4, 5];
const squaredNumbers = numbers.map(number => number * number);
console.log(squaredNumbers); // [1, 4, 9, 16, 25]
In this example, we define an array of numbers and use the map method to create a new array squaredNumbers that contains the squared value of each number in the original array. We pass a callback function to map that returns the squared value of each number.
In React, you often need to manipulate arrays of data to render components, and filter and map are commonly used to transform and filter data before rendering. Asynchronous programming is also an essential part of React, as it allows components to interact with APIs and other data sources without blocking the main thread.
Filter, map, and async/await are not ES6 features, but they are still very important concepts to understand when working with modern web development frameworks such as React.
Subscribe to my newsletter
Read articles from Badreddine Boudaoud directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Badreddine Boudaoud
Badreddine Boudaoud
I'm Badreddine Boudaoud, an Frontend Developer and Technical Writer.I love sharing my knowledge and helping others grow in their skills. Follow me for more articles.