20 mins read

Exploring All Features of ECMAScript 2015 (ES6)

Welcome to our deep dive into ECMAScript 2015 (ES6), where we’ll explore the revolutionary features that transformed JavaScript into a more powerful, expressive, and efficient language.

Why Learning ES6 is Essential Before Using React or Other Modern Frameworks?

JavaScript frameworks like React, Vue, and Angular have revolutionized interactive web development. However, before diving into these tools, it’s crucial to first master JavaScript, particularly ES6 (ECMAScript 2015).

ECMAScript 2015 — ES6

ECMAScript 2015, also known as ES6, is a significant update to the JavaScript language standard, introducing several new features and syntax enhancements that aim to make JavaScript code more expressive and easier to write.

Some of the key features introduced in Es6 include:

  1. let and const Declarations
  2. Arrow Functions
  3. Classes
  4. Template literals or template strings
  5. Destructuring
  6. Enhanced object literals
  7. Default Parameters, Rest Parameters and Spread Syntax
  8. Modules
  9. Promises

1. let and const Declarations

ES6 introduced let and const for variable declarations, offering block-scoped variables and constants, which provide more predictable behavior compared to var.

Let

let is a block-scoped variable.
Here’s an example of using let:

function letExmFunc() {
  if (true) {
    let y = 2;
  }
  console.log(y); // Throws a ReferenceError: y is not defined
}

letExmFunc();

In this example, y is declared using let inside the if block, making it inaccessible outside the block. Attempting to log y outside the block will throw a ReferenceError error.

const

const is also a block-scoped variable used to declare constants that cannot be reassigned after initialization.
Here’s an example of using const:

function constExmFunc() {
  const a = 2
  a = 5 // Throws a TypeError: Assignment to constant variable.
}

constExmFunc();

In this example, a is declared using const and immediately assigned the value 2. When attempting to reassign a to 5, a TypeError is thrown because const variables cannot be reassigned.

2. Arrow Functions

Arrow functions (=>) make writing functions shorter and easier. They automatically handle what this refer to, and can automatically return a value without needing a return statement.

// Traditional function
function multiplyFunc(a, b) {
  return a * b;
}

// Arrow function
const multiplyFuncArrow = (a, b) => a * b;

// Example usage
console.log(multiplyFunc(5, 3)); // Output: 15
console.log(multiplyFuncArrow(5, 3)); // Output: 15

In this example, multiplyFuncArrow is an arrow function that takes two parameters (a and b) and multiplies them together. It achieves the same result as the traditional multiplyFunc function but with a more concise syntax.

3. Classes

ES6 class syntax introduces a more familiar and structured way to create objects and handle inheritance in JavaScript. Here are some examples to illustrate how ES6 classes work:

Basic Class Syntax

// Defining a class
class StudentAge {
  // Constructor method
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  // Method
  methodCalling() {
    console.log('Done');
  }
}

// Creating instances
let studentDetail = new StudentAge('Amitab', 32);

// Accessing properties
console.log(studentDetail.name); // Output: Amitab
console.log(studentDetail.age); // Output: 32

// Calling methods
studentDetail.methodCalling(); // Output: Done

Instance Methods:

// Defining a class
class StudentName {
  // Constructor method
  constructor(name) {
    this.name = name;
  }

  // Method
  methodCalling() {
    console.log('Done');
  }
}

// Child class inheriting from StudentName
class Boy extends StudentName {
  constructor(name, age) {
    super(name); // Call the super class constructor
    this.age = age;
  }

  // Override method
  methodCalling() {
    console.log('Passed');
  }

  // New method specific to Boy class
  fetch() {
    console.log('Fetching...');
  }
}

// Creating instances
let boy = new Boy('Amitab', '32');

// Accessing properties and methods
console.log(boy.name); // Output: Amitab
boy.methodCalling(); // Output: Passed
boy.fetch(); // Output: Fetching...

Static Methods

class MathCalc {
  // Static method
  static add(a, b) {
    return a + b;
  }

  // Static method
  static multiply(a, b) {
    return a * b;
  }
}

// Calling static methods
console.log(MathCalc.add(5, 6)); // Output: 11
console.log(MathCalc.multiply(6, 2)); // Output: 12

Getter and Setter Methods

You can define getter and setter methods using the get and set keywords, which provide controlled access to object properties.

class Quadrate {
  constructor(width, height) {
    this._width = width;
    this._height = height;
  }

  // Getter
  get area() {
    return this._width * this._height;
  }

  // Setter
  set dimensions({ width, height }) {
    this._width = width;
    this._height = height;
  }
}

// Creating instance
let rect = new Quadrate(10, 5);

// Accessing getter
console.log(rect.area); // Output: 50

// Using setter
rect.dimensions = { width: 20, height: 10 };

// Accessing getter again
console.log(rect.area); // Output: 200

These examples illustrate fundamental concepts such as class definition, inheritance, static methods, and the utilization of getters and setters within ES6 classes. Classes offer a structured approach to managing objects in JavaScript, particularly valuable in complex applications that benefit from well-defined object-oriented patterns.

4. Template literals or template strings

Template literals offer a convenient syntax for creating strings that incorporate variables and expressions directly, eliminating the need for concatenation. Below are examples illustrating their usage:

Basic template literal

// Basic template literal
const name = 'Amitab';
const greeting = `Hello, ${name}!`;

console.log(greeting); // Output: Hello, Amitab

Multiline Strings

// Multiline string using template literal
const multiline = `This is a multiline
string using template literals.
It can span multiple lines.`;

console.log(multiline);
/*
Output:
This is a multiline
string using template literals.
It can span multiple lines.
*/

Expression Interpolation

// Expression interpolation
const a = 8;
const b = 10;
const sum = `Sum of ${a} and ${b} is ${a + b}.`;

console.log(sum); // Output: Sum of 8 and 10 is 18.

Tagged Templates

In JavaScript, tagged templates can process template literals using a function. This function, often called a “tag function,” allows you to manipulate the template literal and its interpolated values before they are output as a final string.

// Tagged template example
function tagFunction(strings, ...values) {
  console.log(strings); // Array of string literals
  console.log(values); // Array of interpolated values
  return 'Processed string'; // Can return processed string
}

const a = 10;
const b = 8;
const processedString = tagFunction`Sum of ${a} and ${b} is ${a + b}.`;

console.log(processedString); // Output: Processed string

Nesting template literals

Template literals can be nested within each other for more complex string constructions.

// Nesting template literals
const name = 'Amitab';
const greeting = `Hello, ${`Mr. ${name.toUpperCase()}`}!`;

console.log(greeting); // Output: Hello, Mr. Amitab!

Use Cases

Template literals are particularly useful in scenarios where you need to:

  • Construct strings dynamically with variables and expressions.
  • Create multiline strings easily.
  • Improve readability and maintainability compared to traditional string concatenation.

These features make template literals a powerful addition to JavaScript for handling strings more flexibly and expressively.

5. Destructuring

Destructuring in ES6 (ECMAScript 2015) is a powerful feature that simplifies extracting values from arrays or properties from objects into separate variables. It offers a more concise syntax for accessing data from arrays and objects compared to traditional methods.

Array Destructuring

Array destructuring allows you to unpack values from arrays or iterable objects into separate variables. Here’s a basic example:

// Basic array destructuring
let numbers = [1, 2, 3, 4, 5];
let [a, b, c, d, e] = numbers;

console.log(a); // Output: 1
console.log(b); // Output: 2
console.log(c); // Output: 3
console.log(d); // Output: 4
console.log(e); // Output: 5

You can also skip elements by omitting variables:

// Skipping elements in array destructuring
let [first, , third] = numbers;

console.log(first);  // Output: 1
console.log(third);  // Output: 3

Object Destructuring

Object destructuring enables you to extract properties from objects and assign them to variables with corresponding names:

// Basic object destructuring
let person = {
  name: 'Amitab',
  age: 30,
  city: 'Mumbai'
};

let { name, age } = person;

console.log(name);  // Output: 'Amitab'
console.log(age);   // Output: 30

// Assigning to different variable names
let { name: personName, age: personAge } = person;

console.log(personName);  // Output: 'Amitab'
console.log(personAge);   // Output: 30

// Assigning default values
let { name: userName, isAdmin = false } = { name: 'Uday' };

console.log(userName);  // Output: 'Uday'
console.log(isAdmin);   // Output: false

Destructuring Parameters:

Destructuring can also be used in function parameters:

// Destructuring in function parameters
function userDetail({ name, age }) {
  console.log(`${name} is ${age} years old`);
}

let user = {
  name: 'Amitab',
  age: 25
};

userDetail(user);  // Output: 'Amitab is 25 years old'

Nested Destructuring:

// Nested destructuring
let nestedObject = {
  start: { x: 5, y: 10 },
  end: { x: 10, y: 20 }
};

let { start: { x: startX, y: startY }, end: { x: endX, y: endY } } = nestedObject;

console.log(startX, startY);  // Output: 5 10
console.log(endX, endY);      // Output: 10 20

Destructuring in ES6 improves not only convenience but also enhances readability by making your code more expressive and concise when dealing with arrays, objects, and function parameters.

6. Enhanced object literals

Enhanced object literals are a set of features introduced in ECMAScript 2015 (ES6) that simplify working with objects in JavaScript. These enhancements improve the syntax and capabilities of object literals, which are fundamental in defining objects in JavaScript.

Key features of enhanced object literals include:

Property Shorthand:

When the property key and variable name are the same, you can omit the key in object literal declarations. For example:

let name = 'Amitab'
let age = 30;

// Without enhanced object literals
let person = { name: name, age: age };

// With enhanced object literals
let person = { name, age };

Method Shorthand:

When defining methods in an object literal, you can omit the function keyword. For example:

// Without enhanced object literals
let obj = {
  method: function() {
    // Method definition
  }
};

// With enhanced object literals
let obj = {
  method() {
    // Method definition
  }
};

Computed Property Names:

You can compute the property name of an object literal using an expression enclosed in square brackets ([]). For example:

let propKey = 'name'

// Without enhanced object literals
let obj = {};
obj[propKey] = 'Amitab'

// With enhanced object literals
let obj = {
  [propKey]: 'Amitab'
};

Object Method Definitions:

Methods can be defined directly on the object prototype using concise syntax, which is particularly advantageous when working with classes:

let obj = {
  foo() {
    // Method definition
  },
  bar() {
    // Method definition
  }
};

Enhanced object literals enhance code readability and conciseness when handling objects in JavaScript. They are widely embraced in modern JavaScript development for their simplicity and expressive capabilities.

7. Default Parameters, Rest Parameters and Spread Syntax

In JavaScript, the terms “default,” “rest,” and “spread” denote distinct functionalities associated with functions and arrays. Let’s explore each concept individually:

Default Parameters

Default parameters in JavaScript enable you to initialize a function with default values in case the arguments are not provided or are explicitly set to undefined. This feature was introduced as part of ECMAScript 2015 (ES6).

function multiply(a, b = 1) {
  return a * b;
}

console.log(multiply(5, 3)); // Output: 15
console.log(multiply(5));    // Output: 5 (uses default value of b)

Rest Parameters

Rest parameters (...) in JavaScript enable a function to accept an unlimited number of arguments as an array. This feature gathers up any remaining parameters into a single array, which proves especially useful when the exact number of arguments is unknown beforehand.

function sum(...args) {
  return args.reduce((acc, val) => acc + val, 0);
}

console.log(sum(1, 2, 3, 4, 5)); // Output: 15
console.log(sum(1, 2));          // Output: 3

Spread syntax

Spread syntax (...) in JavaScript is utilized to expand elements of an iterable (such as an array or string) into places where multiple elements are expected, such as function arguments or array literals. This syntax allows an iterable, like an array expression or string, to be expanded in locations where zero or more elements or arguments are anticipated.

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];
console.log(combined); // Output: [1, 2, 3, 4, 5, 6]

// Cloning arrays
const original = [10, 20, 30];
const copy = [...original];
console.log(copy); // Output: [10, 20, 30]

Summary

  • Default parameters provide initial values for function parameters.
  • Rest parameters gather remaining function arguments into an array.
  • Spread syntax allows iterables to be expanded into multiple elements.

8. Modules

ES6 modules are a standardized method for organizing and encapsulating JavaScript code into reusable components. They promote a modular programming approach by enabling developers to divide their code into smaller files, each dedicated to specific functionalities. ES6 modules offer support for both named exports (exporting multiple values from a module) and default exports (exporting a single value), which enhances code clarity and maintainability in contemporary JavaScript applications.

Module Basics:

Exporting: Modules use the export keyword to export functionalities, variables, or classes. They support named exports, default exports, or a combination of both.

// exampleModule.js
export function multiply(a, b) {
    return a * b;
}

Importing: Modules can import exported values from other modules using the import keyword. Imports can target specific exports, all exports, or a default export from a module.

// main.js
import { multiply } from './exampleModule.js';
console.log(multiply(3, 4)); // Outputs: 12

Types of Exports

Named Exports: Exporting specific functions or variables by name.

// exampleModule.js
export function square(x) {
    return x * x;
}

export const pi = 3.141592;

Default Exports: Exporting a single value as the default export of a module.

// exampleModule.js
export default function cube(x) {
    return x * x * x;
}


// main.js
import cube from './exampleModule.js';

console.log(cube(3)); // Outputs: 27

Advanced Module Features:

Namespace Imports: Importing all exports from a module into a single namespace.

import * as example from './exampleModule.js';
console.log(example.multiply(3, 4));

Dynamic Imports: Importing modules dynamically at runtime.

import('./exampleModule.js')
    .then(module => {
        console.log(module.multiply(3, 4));
    });

ES6 modules provide a clean and standardized way to organize and share code in JavaScript applications, replacing the previous approaches like CommonJS and AMD (Asynchronous Module Definition). They improve performance and maintainability by encouraging encapsulation and explicit dependency declaration.

9. Promises

Promises are a core concept in JavaScript for managing asynchronous operations. Introduced in ES6, promises offer a cleaner and more flexible alternative to callback-based approaches for managing asynchronous code. Here’s a detailed overview of promises:

Creating a Promise

To create a new Promise, use the Promise constructor, which requires an executor function with two parameters: resolve and reject.

const myPromise = new Promise((resolve, reject) => {
  // Perform some asynchronous operation
  let success = true;

  if (success) {
    resolve('Operation was successful');
  } else {
    reject('Operation failed');
  }
});

Using Promises

Once a Promise is created, you can use its .then() and .catch() methods to handle resolved and rejected outcomes, respectively.

myPromise
  .then(result => {
    console.log(result); // Logs: 'Operation was successful'
  })
  .catch(error => {
    console.error(error); // Logs: 'Operation failed'
  });

Chaining Promises

Promises can be chained to run asynchronous tasks one after another. Each .then() returns a new Promise, allowing you to chain multiple .then() calls.

myPromise
  .then(result => {
    console.log(result);
    return 'Next step';
  })
  .then(nextResult => {
    console.log(nextResult); // Logs: 'Next step'
  })
  .catch(error => {
    console.error(error);
  });

Promise.all()

Promise.all() is used to run multiple promises in parallel and wait for all of them to complete. It returns a single Promise that resolves when all the input promises have resolved.

const promise1 = Promise.resolve('First');
const promise2 = Promise.resolve('Second');
const promise3 = Promise.resolve('Third');

Promise.all([promise1, promise2, promise3])
  .then(results => {
    console.log(results); // Logs: ['First', 'Second', 'Third']
  })
  .catch(error => {
    console.error(error);
  });

Promise.race()

Promise.race() returns a Promise that resolves or rejects as soon as one of the promises in the iterable resolves or rejects. It effectively “races” the promises, settling with the outcome of the first one.

const promise1 = new Promise((resolve, reject) => setTimeout(resolve, 500, 'First'));
const promise2 = new Promise((resolve, reject) => setTimeout(resolve, 100, 'Second'));

Promise.race([promise1, promise2])
  .then(result => {
    console.log(result); // Logs: 'Second' (because it resolved first)
  })
  .catch(error => {
    console.error(error);
  });

Promise.allSettled()

Promise.allSettled() returns a Promise that resolves after all of the given Promises have either resolved or rejected. It provides an array of objects that each describe the outcome of each Promise.

const promise1 = Promise.resolve('First');
const promise2 = Promise.reject('Second');
const promise3 = Promise.resolve('Third');

Promise.allSettled([promise1, promise2, promise3])
  .then(results => {
    console.log(results);
    // Logs:
    // [
    //   { status: 'fulfilled', value: 'First' },
    //   { status: 'rejected', reason: 'Second' },
    //   { status: 'fulfilled', value: 'Third' }
    // ]
  });

Summary

Promises are a powerful feature in ES6 that make asynchronous programming more manageable and readable. They provide a structured way to handle asynchronous operations, avoid callback hell, and improve error handling. By understanding and using Promises effectively, you can write cleaner, more maintainable code.

In our next blog, we’ll dive into ECMAScript 7 and uncover its new features. Stay tuned as we explore the next chapter in the evolution of JavaScript!

FAQs

let and const are new ways to declare variables in ES6:

  • let: Creates a block-scoped variable.
  • const: Creates a block-scoped variable that cannot be reassigned after initialization.

Arrow functions provide a shorter syntax for writing functions and do not have their own this context. They are defined using the => syntax.

const add = (a, b) => a + b;

Template literals are string literals that allow for embedded expressions and multi-line strings, using backticks (`) and ${} for interpolation.

const name = 'Alice';
const greeting = `Hello, ${name}!`;

Destructuring is a syntax for unpacking values from arrays or objects into distinct variables.

Array Destructuring:const [a, b] = [1, 2];

Object Destructuring:const { x, y } = { x: 10, y: 20 };

Default parameters allow functions to be called with fewer arguments than they are defined to accept, by providing default values.

function greet(name = 'Guest') {
return `Hello, ${name}!`;
}

The rest parameter (...) allows a function to accept an arbitrary number of arguments as an array.

function sum(...numbers) {
return numbers.reduce((acc, num) => acc + num, 0);
}

The spread operator (...) expands elements of an array or object into individual elements.

Example with Arrays:
const nums = [1, 2, 3];
const moreNums = [...nums, 4, 5];

Example with Objects:
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 };

ES6 classes provide a syntactic sugar for creating objects and dealing with inheritance in a more structured way than prototype-based methods.
class Person {
constructor(name) {
this.name = name;
}

greet() {
return `Hello, my name is ${this.name}`;
}
}

ES6 modules allow you to export and import code between files using export and import statements, promoting better code organization and reusability.

Exporting:
// math.js
export const add = (a, b) => a + b;

Importing:
// app.js
import { add } from './math';

Promises are objects representing the eventual completion or failure of an asynchronous operation. They allow handling asynchronous results more effectively than callbacks.

const fetchData = () => new Promise((resolve, reject) => {
setTimeout(() => resolve('Data fetched'), 1000);
});

fetchData().then(data => console.log(data)).catch(err => console.error(err));

Symbol is a new primitive data type used to create unique and immutable identifiers, often for object property keys to avoid naming collisions.

const uniqueId = Symbol('id');
const obj = { [uniqueId]: 123 };

Iterators are objects that conform to the iterator protocol, providing a next method. Generators are functions that use yield to produce sequences of values, allowing custom iteration behavior.

Generator Example:

function* gen() {
yield 1;
yield 2;
yield 3;
}

const iterator = gen();
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
console.log(iterator.next().value); // 3

6 thoughts on “Exploring All Features of ECMAScript 2015 (ES6)

Leave a Reply

Your email address will not be published. Required fields are marked *