JavaScript has come a long way since its inception in 1995.
The language has evolved significantly, and its modern features have transformed the way developers write code.
This comprehensive guide will explore the latest features introduced in ECMAScript 6 (ES6) and beyond, including in-depth tutorials, code samples, examples, and scenarios to help you master advanced JavaScript techniques.
π Let’s dive in and explore the world of modern JavaScript!
ES6 (ECMAScript 2015) Features
ES6, also known as ECMAScript 2015, introduced several groundbreaking features that have become essential for modern JavaScript development.
In this section, we will cover the most important features of ES6, providing examples and explanations.
Let and Const
Before ES6, developers used the var
keyword to declare variables. ES6 introduced the let
and const
keywords, which offer better control over variable scoping.
// Using var
for (var i = 0; i < 5; i++) {
console.log(i);
}
console.log(i); // Output: 5 (i is accessible outside the loop)
// Using let
for (let j = 0; j < 5; j++) {
console.log(j);
}
console.log(j); // ReferenceError: j is not defined (j is not accessible outside the loop)
const is used to declare constants, i.e., variables that cannot be reassigned:
const PI = 3.14159;
PI = 3.14; // TypeError: Assignment to constant variable.
Template Literals
Template literals simplify string concatenation and interpolation:
const name = "John";
const age = 30;
// ES5 string concatenation
console.log("Hello, my name is " + name + " and I am " + age + " years old.");
// ES6 template literals
console.log(`Hello, my name is ${name} and I am ${age} years old.`);
Arrow Functions
Arrow functions provide a more concise syntax for defining functions, and they also lexically bind the this
keyword:
// ES5 function
const square = function (x) {
return x * x;
};
// ES6 arrow function
const square = (x) => {
return x * x;
};
// ES6 arrow function with implicit return
const square = (x) => x * x;
Destructuring
Destructuring allows you to extract values from arrays or properties from objects into distinct variables:
// Array destructuring
const numbers = [1, 2, 3];
const [a, b, c] = numbers; // a = 1, b = 2, c = 3
// Object destructuring
const person = { name: "Alice", age: 25 };
const { name, age } = person; // name = "Alice", age = 25
Default Parameters
Default parameters allow you to specify default values for function arguments:
// ES5 default parameters
function greet(name) {
name = name || "Guest";
console.log("Hello, " + name + "!");
}
// ES6 default parameters
function greet(name = "Guest") {
console.log(`Hello, ${name}!`);
}
Enhanced Object Literals
ES6 introduced a more concise syntax for defining object literals:
const x = 10;
const y = 20;
const point = {
x, // Same as x: x
y, // Same as y: y
draw() {
// Same as draw: function() {...}
console.log(`Drawing point at (${this.x}, ${this.y})`);
},
};
Rest and Spread Operators
The rest operator (...
) allows you to represent an indefinite number of arguments as an array:
function sum(...numbers) {
return numbers.reduce((acc, n) => acc + n, 0);
}
console.log(sum(1, 2, 3, 4, 5)); // Output: 15
The spread operator allows you to expand arrays or objects:
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const arr3 = [...arr1, ...arr2]; // Output: [1, 2, 3, 4, 5, 6]
Modules
ES6 introduced a module system that allows you to import and export functions, objects, or values:
// utils.js
export function greet(name) {
console.log(`Hello, ${name}!`);
}
// main.js
import { greet } from "./utils.js";
greet("John");
Promises
Promises provide a more elegant way to handle asynchronous operations:
const getData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Data received");
}, 1000);
});
};
getData()
.then((data) => {
console.log(data); // Output: "Data received"
})
.catch((error) => {
console.log(error);
});
Classes
ES6 introduced classes, providing a more intuitive way to work with object-oriented programming:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}
const john = new Person("John", 30);
john.greet(); // Output: "Hello, my name is John and I am 30 years old."
ES7 (ECMAScript 2016) Features
ES7, also known as ECMAScript 2016, introduced a couple of useful features:
Exponentiation Operator
The exponentiation operator (**
) raises a number to the power of another number:
const x = 2 ** 3; // Output: 8 (2 raised to the power of 3)
Array.prototype.includes
Array.prototype.includes
checks if an array includes a certain value:
const numbers = [1, 2, 3, 4, 5];
console.log(numbers.includes(3)); // Output: true
ES8 (ECMAScript 2017) Features
ES8, or ECMAScript 2017, brought additional features to the language:
Async/Await
async/await
makes it easier to work with Promises, providing a more readable and synchronous-looking code:
const getData = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve("Data received");
}, 1000);
});
};
(async () => {
const data = await getData();
console.log(data); // Output: "Data received"
})();
Object.entries and Object.values
Object.entries
returns an array of a given object’s key-value pairs, while Object.values
returns an array of the object’s values:
const person = { name: "Alice", age: 25 };
console.log(Object.entries(person)); // Output: [["name", "Alice"], ["age", 25]]
console.log(Object.values(person)); // Output: ["Alice", 25]
String Padding
String.prototype.padStart
and String.prototype.padEnd
pad a string with a specified character:
const str = "42";
console.log(str.padStart(5, "0")); // Output: "00042"
console.log(str.padEnd(5, "0")); // Output: "42000"
Trailing Commas
Trailing commas are now allowed in function parameter lists and calls:
function foo(a, b, c,) {
// ...
}
foo(1, 2, 3,);
ES9 (ECMAScript 2018) Features
ES9, or ECMAScript 2018, added further enhancements to JavaScript:
Rest/Spread Properties
Rest/Spread properties allow you to use the rest and spread operators with objects:
const person = { name: "Alice", age: 25, country: "USA" };
const { country, ...rest } = person;
console.log(country); // Output: "USA"
console.log(rest); // Output: { name: "Alice", age: 25 }
Promise.prototype.finally
Promise.prototype.finally
allows you to run a callback when a Promise is settled, regardless of whether it is fulfilled or rejected:
fetch("https://api.example.com/data")
.then((response) => console.log(response))
.catch((error) => console.error(error))
.finally(() => console.log("Request completed"));
Asynchronous Iteration
Asynchronous iteration allows you to use for-await-of
loops with asynchronous data sources:
async function* asyncGenerator() {
yield Promise.resolve(1);
yield Promise.resolve(2);
yield Promise.resolve(3);
}
(async () => {
for await (const num of asyncGenerator()) {
console.log(num); // Output: 1, 2, 3 (one at a time, with a delay)
}
})();
RegExp Enhancements
ES9 introduced several enhancements to regular expressions, such as named capture groups, Unicode property escapes, and lookbehind assertions:
// Named capture groups
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = regex.exec("2023-04-08");
console.log(match.groups); // Output: { year: "2023", month: "04", day: "08" }
// Unicode property escapes
const unicodeRegex = /\p{Script=Latin}/u;
console.log(unicodeRegex.test("ΓΌ")); // Output: true
// Lookbehind assertions
const lookbehindRegex = /(?<=\$)\d+/;
const match2 = lookbehindRegex.exec("The price is $42");
console.log(match2[0]); // Output: "42"
ES10 (ECMAScript 2019) Features
ES10, or ECMAScript 2019, continued to expand the capabilities of JavaScript:
Array.prototype.flat and flatMap
Array.prototype.flat
flattens an array up to the specified depth:
const nestedArray = [1, [2, [3, [4]], 5]];
console.log(nestedArray.flat(2)); // Output: [1, 2, 3, [4], 5]
Array.prototype.flatMap
combines mapping and flattening into one operation:
const numbers = [1, 2, 3];
console.log(numbers.flatMap((x) => [x, x * 2])); // Output: [1, 2, 2, 4, 3, 6]
Object.fromEntries
Object.fromEntries
creates an object from an array of key-value pairs:
const entries = [["name", "Alice"], ["age", 25]];
const obj = Object.fromEntries(entries);
console.log(obj); // Output: { name: "Alice", age: 25 }
String.prototype.trimStart and trimEnd
String.prototype.trimStart
and String.prototype.trimEnd
remove whitespace characters from the start or end of a string, respectively:
const str = " Hello, world! ";
console.log(str.trimStart()); // Output: "Hello, world! "
console.log(str.trimEnd()); // Output: " Hello, world!"
Optional Catch Binding
Optional catch binding allows you to omit the error parameter in a catch block:
try {
// ...
} catch {
console.log("An error occurred");
}
ES11 (ECMAScript 2020) Features
ES11, or ECMAScript 2020, introduced several new features to JavaScript:
Nullish Coalescing Operator
The nullish coalescing operator (??
) returns the right-hand operand when the left-hand operand is null
or undefined
; otherwise, it returns the left-hand operand:
const x = null;
const y = "Hello";
console.log(x ?? "Default"); // Output: "Default"
console.log(y ?? "Default"); // Output: "Hello"
Optional Chaining
Optional chaining (?.
) allows you to access deeply nested properties without having to check for the existence of each property in the chain:
const person = { name: "Alice", address: { city: "New York" } };
console.log(person.address?.city); // Output: "New York"
console.log(person.email?.domain); // Output: undefined (no error)
BigInt
BigInt is a new primitive type that can represent integers of arbitrary size:
const bigNumber = 1234567890123456789012345678901234567890n;
console.log(bigNumber + 1n); // Output: 1234567890123456789012345678901234567891n
Promise.allSettled
Promise.allSettled
returns a promise that resolves when all promises in the input iterable have settled, whether they are fulfilled or rejected:
const promise1 = Promise.resolve("Success");
const promise2 = Promise.reject("Error");
Promise.allSettled([promise1, promise2]).then((results) => {
console.log(results);
// Output: [{ status: "fulfilled", value: "Success" }, { status: "rejected", reason: "Error" }]
});
Dynamic Import
Dynamic import allows you to load modules on demand using the import()
function:
(async () => {
const { greet } = await import("./utils.js");
greet("John");
})();
globalThis
globalThis
provides a single, global object that can be accessed across different JavaScript environments:
console.log(globalThis.Math === Math); // Output: true
ES12 (ECMAScript 2021) Features
ES12, or ECMAScript 2021, introduced more features to JavaScript:
Numeric Separators
Numeric separators (_
) make large numbers more readable:
const largeNumber = 1_000_000_000;
console.log(largeNumber); // Output: 1000000000
Logical Assignment Operators
Logical assignment operators combine logical operations with assignment:
let x = null;
x ||= "default";
console.log(x); // Output: "default"
String.prototype.replaceAll
String.prototype.replaceAll
replaces all occurrences of a substring with a new substring:
const str = "Hello world, world!";
console.log(str.replaceAll("world", "friend")); // Output: "Hello friend, friend!"
Promise.any
Promise.any
returns a promise that fulfills as soon as one of the promises in the input iterable fulfills:
const promise1 = new Promise((_, reject) => setTimeout(reject, 100, "Error 1"));
const promise2 = new Promise((resolve) => setTimeout(resolve, 200, "Success 2"));
const promise3 = new Promise((_, reject) => setTimeout(reject, 300, "Error 3"));
Promise.any([promise1, promise2, promise3])
.then((value) => {
console.log(value); // Output: "Success 2"
})
.catch((error) => {
console.log(error);
});
WeakRefs
WeakRefs allow you to create a weak reference to an object, which does not prevent the object from being garbage collected:
class MyClass {
constructor(id) {
this.id = id;
}
}
const weakRef = new WeakRef(new MyClass(1));
// The object is still accessible as long as it hasn't been garbage collected
console.log(weakRef.deref()?.id); // Output: 1
FinalizationRegistry
FinalizationRegistry
provides a way to register a cleanup callback that runs when an object is garbage collected:
const registry = new FinalizationRegistry((id) => {
console.log(`Object with id ${id} was garbage collected`);
});
const object = new MyClass(2);
registry.register(object, object.id);
// After the object is garbage collected, the callback will be invoked
Summary
This comprehensive guide covers modern JavaScript features from ES6 and beyond, providing you with the knowledge and skills to write clean, efficient, and professional JavaScript code.
By understanding and utilizing these features, you can stay up-to-date with the latest best practices and create sophisticated web applications that perform optimally.
Happy coding! π
Thank you for reading our blog, we hope you found the information provided helpful and informative. We invite you to follow and share this blog with your colleagues and friends if you found it useful.
Share your thoughts and ideas in the comments below. To get in touch with us, please send an email to dataspaceconsulting@gmail.com or contactus@dataspacein.com.
You can also visit our website β DataspaceAI