ES6 for Beginners: Getting Started with Modern JavaScript

Table of contents

Why ES6?

ES6 in the JavaScript was introduced with variety of new features to make coding easier and more efficient. Before ES6, JavaScript had some limitations that made certain tasks harder to accomplish. ES6 addresses these limitations by adding new syntax and features that improve the language's capabilities.

In this blog we are going to learn some of the important topics of ES6 before starting with the ES6 you should have the basic knowledge of the javascript.

The topic that we are going to learn in the ES6 are

  • Better Variable Declarations: let and const

  • Shorter Functions: Arrow Functions

  • Template Literals

  • Default Parameters

  • Destructuring Assignment

  • Enhanced Object Handling: Rest and Spread Operators

  • Promises for Asynchronous Operations

let and const in Detail

let and const are two ways to declare variables in JavaScript, introduced in ES6. They provide more control over the scope and mutability of variables compared to the older var keyword.

let

  • Block-scoped: Variables declared with let are limited to the block (enclosed by {}) in which they are declared.

  • Reassignable: You can reassign values to variables declared with let.

Example 1: Block Scope
if (true) {
  let x = 10;
  console.log(x); // 10
}
console.log(x); // ReferenceError: x is not defined

In this example, x is only accessible within the if block.

Example 2: Reassignment
let y = 20;
y = 30;
console.log(y); // 30

Here, y is declared with let and can be reassigned to a new value.

Example 3: Loop Example
for (let i = 0; i < 5; i++) {
  console.log(i); // 0, 1, 2, 3, 4
}
console.log(i); // ReferenceError: i is not defined

The variable i is block-scoped to the for loop, and it’s not accessible outside the loop.

const

  • Block-scoped: Similar to let, variables declared with const are limited to the block in which they are declared.

  • Not Reassignable: Once a variable is declared with const, it cannot be reassigned a new value. However, if the variable is an object or an array, its properties or elements can be modified.

Example 1: Block Scope
if (true) {
  const a = 100;
  console.log(a); // 100
}
console.log(a); // ReferenceError: a is not defined

Like let, a is only accessible within the if block.

Example 2: No Reassignment
const b = 50;
b = 60; // TypeError: Assignment to constant variable

Arrow Functions in ES6

Arrow functions are a new way to write functions in JavaScript introduced in ES6. They provide a shorter and cleaner syntax compared to traditional function expressions. Let's break down what arrow functions are and how to use them with easy examples.

Syntax

An arrow function is defined using a pair of parentheses for parameters, followed by an arrow (=>), and then the function body.

Basic Syntax
const functionName = (parameters) => {
  // function body
};

If the function body contains only a single expression, you can omit the curly braces and the return keyword.

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

Key Features of Arrow Functions

  1. Shorter Syntax: Arrow functions are more concise, especially for simple functions.

  2. Implicit Return: If the function body has only one expression, it returns the result of that expression automatically without needing the return keyword.

Examples

Example 1: Simple Arrow Function

A simple example to add two numbers:

// Regular function
function add(a, b) {
  return a + b;
}

// Arrow function
const add = (a, b) => a + b;

console.log(add(2, 3)); // Output: 5
Example 2: Arrow Function with No Parameters

An arrow function that returns a fixed value:

const greet = () => 'Hello, World!';
console.log(greet()); // Output: Hello, World!
Example 3: Arrow Function with One Parameter

If there's only one parameter, you can omit the parentheses:

const square = x => x * x;
console.log(square(4)); // Output: 16
Example 4: Arrow Function with Multiple Lines

If the function body contains multiple lines, use curly braces and the return keyword:

const multiply = (a, b) => {
  const result = a * b;
  return result;
};

console.log(multiply(3, 4)); // Output: 1

Summary

Arrow functions in ES6 provide a more concise and readable way to write functions. They are especially useful for short functions, callbacks, and maintaining the this context. By using arrow functions, you can write cleaner and more efficient JavaScript code.

Template Literals in JavaScript (ES6)

Template literals are a new way to work with strings in JavaScript, introduced in ES6. They make it easier to create and manipulate strings, especially when you need to include variables or expressions within a string.

Key Features of Template Literals

  1. Backticks ( ) Instead of Quotes: Template literals are enclosed by backticks instead of single (') or double (") quotes.

  2. Embedded Expressions: You can embed expressions inside a template literal using ${expression}.

  3. Multiline Strings: Template literals allow for multiline strings without needing escape characters.

Basic Syntax

const greeting = `Hello, world!`;

Embedded Expressions

You can include variables and expressions directly within the string:

const name = "Alice";
const greeting = `Hello, ${name}!`;
console.log(greeting); // Output: Hello, Alice!

You can also include more complex expressions:

const a = 5;
const b = 10;
const result = `The sum of ${a} and ${b} is ${a + b}.`;
console.log(result); // Output: The sum of 5 and 10 is 15.

Multiline Strings

Template literals make it easy to create strings that span multiple lines:

const message = `This is a message
that spans multiple
lines.`;
console.log(message);

Output:

This is a message
that spans multiple
lines.

Combining Everything

Let's combine embedded expressions and multiline strings:

const name = "Alice";
const age = 25;
const introduction = `My name is ${name}.
I am ${age} years old.
Next year, I will be ${age + 1} years old.`;
console.log(introduction);

Output:

My name is Alice.
I am 25 years old.
Next year, I will be 26 years old.

Practical Example

Suppose you're building a webpage and you want to create a greeting message based on user input:

const userName = "Bob";
const welcomeMessage = `Welcome, ${userName}!
We're glad to have you here.
Feel free to explore our website.`;

console.log(welcomeMessage);

Output:

Welcome, Bob!
We're glad to have you here.
Feel free to explore our website.

Summary

  • Template Literals: Enclose strings in backticks ( ) instead of quotes.

  • Embedded Expressions: Use ${expression} to include variables or expressions.

  • Multiline Strings: Template literals naturally handle multiline strings.

Template literals provide a more readable and convenient way to handle strings, making your JavaScript code cleaner and easier to write.

Default Parameters in JavaScript (ES6)

Default parameters allow you to set default values for function parameters. This means if no value or undefined is passed for a parameter, the default value will be used instead.

Why Use Default Parameters?

Before ES6, you had to write extra code to provide default values for function parameters. With default parameters, your code becomes cleaner and more concise.

Basic Syntax

The default value is assigned using the = operator in the function declaration:

function functionName(parameter = defaultValue) {
  // function body
}

Examples

Example 1: Simple Default Parameter
function greet(name = 'Stranger') {
  return `Hello, ${name}!`;
}

console.log(greet('Alice')); // Output: Hello, Alice!
console.log(greet());        // Output: Hello, Stranger!

In this example, if no argument is provided for name, it defaults to 'Stranger'.

Example 2: Multiple Default Parameters

You can provide default values for multiple parameters:

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

console.log(multiply(5, 2));  // Output: 10
console.log(multiply(5));     // Output: 5
console.log(multiply());      // Output: 1

Here, both a and b have default values of 1. If only one argument is provided, the other parameter uses its default value.

Example 3: Using Expressions as Default Values

Default values can be expressions, including other variables or function calls:

function getDefaultAge() {
  return 18;
}

function createUser(name = 'Guest', age = getDefaultAge()) {
  return `Name: ${name}, Age: ${age}`;
}

console.log(createUser('Alice', 25));  // Output: Name: Alice, Age: 25
console.log(createUser('Alice'));      // Output: Name: Alice, Age: 18
console.log(createUser());             // Output: Name: Guest, Age: 18

In this example, the age parameter defaults to the value returned by getDefaultAge() if no age is provided.

Example 4: Default Parameters and undefined

If undefined is explicitly passed as an argument, the default value will be used:

function sayHello(name = 'Friend') {
  return `Hello, ${name}!`;
}

console.log(sayHello('Alice'));    // Output: Hello, Alice!
console.log(sayHello(undefined));  // Output: Hello, Friend!
console.log(sayHello(null));       // Output: Hello, null!

In this example, passing undefined triggers the default value, but passing null does not.

Practical Example

Consider a function to create a greeting message, where the default greeting is "Hello" and the default name is "World":

function createGreeting(greeting = 'Hello', name = 'World') {
  return `${greeting}, ${name}!`;
}

console.log(createGreeting('Hi', 'Alice')); // Output: Hi, Alice!
console.log(createGreeting('Hi'));          // Output: Hi, World!
console.log(createGreeting());              // Output: Hello, World!

Summary

  • Default Parameters: Allow you to set default values for function parameters.

  • Simplify Code: Reduces the need for additional code to handle missing arguments.

  • Usage: Provide default values directly in the function declaration.

Default parameters make functions more flexible and help avoid errors by ensuring that parameters always have valid values, simplifying function calls and improving code readability.

Callback Functions in JavaScript

A callback function is a function that is passed as an argument to another function and is executed after some operation or event. Callbacks are a fundamental concept in JavaScript and are used to handle asynchronous operations, events, and more.

How Callbacks Work

  1. Passing a Function as an Argument: You provide a function as an argument to another function.

  2. Execution: The function you passed (the callback) is called at some point within the outer function, usually after a specific task or event.

Why Use Callbacks?

Callbacks allow you to execute code after certain tasks are completed, enabling you to handle asynchronous operations (like loading data) or manage events (like user interactions).

Example of a Simple Callback

Let’s start with a basic example to illustrate how callbacks work:

// A function that accepts another function as a callback
function processData(data, callback) {
  console.log('Processing data:', data);
  callback(); // Execute the callback function
}

// A callback function
function done() {
  console.log('Processing is complete!');
}

// Using the processData function with the done callback
processData('MyData', done);

Explanation:

  1. processData takes two arguments: data and callback.

  2. Inside processData, callback() is called after processing the data.

  3. done is passed as the callback function, which gets executed after the data processing is logged.

Output:

Processing data: MyData
Processing is complete!

Example with Asynchronous Operations

Callbacks are especially useful with asynchronous operations. Here’s an example using a simulated asynchronous operation with setTimeout:

// A function that simulates an asynchronous task
function fetchData(callback) {
  setTimeout(() => {
    const data = 'Fetched Data';
    callback(data); // Execute the callback with data
  }, 1000); // Simulate a delay of 1 second
}

// A callback function to handle the fetched data
function handleData(data) {
  console.log('Data received:', data);
}

// Using fetchData with handleData as the callback
fetchData(handleData);

Explanation:

  1. fetchData simulates fetching data with a delay.

  2. After 1 second, the handleData callback is called with the fetched data.

  3. handleData logs the received data.

Output (after 1 second):

Data received: Fetched Data

Summary

  • Callback Function: A function passed as an argument to another function to be executed later.

  • Purpose: Used to handle asynchronous operations, manage events, and more.

  • Examples: Simple callbacks, asynchronous tasks with setTimeout, event handling.

Callbacks provide a way to execute code at the right time, making JavaScript code more dynamic and responsive to events and data changes.

Destructuring Assignment in JavaScript (ES6)

Destructuring assignment is a feature in JavaScript that allows you to unpack values from arrays or properties from objects into distinct variables. It simplifies the process of extracting values and can make your code more readable.

Array Destructuring

With array destructuring, you can extract values from an array and assign them to variables in a single statement.

Basic Syntax
const [variable1, variable2, ...rest] = array;
Examples
  1. Simple Array Destructuring

     const numbers = [1, 2, 3];
     const [first, second, third] = numbers;
    
     console.log(first);  // Output: 1
     console.log(second); // Output: 2
     console.log(third);  // Output: 3
    
  2. Skipping Values

     const colors = ['red', 'green', 'blue'];
     const [, , thirdColor] = colors;
    
     console.log(thirdColor); // Output: blue
    
  3. Rest Element

     const fruits = ['apple', 'banana', 'cherry', 'date'];
     const [firstFruit, secondFruit, ...restFruits] = fruits;
    
     console.log(firstFruit);  // Output: apple
     console.log(secondFruit); // Output: banana
     console.log(restFruits); // Output: ['cherry', 'date']
    

Object Destructuring

With object destructuring, you can extract properties from objects and assign them to variables.

Basic Syntax
const { property1, property2, ...rest } = object;
Examples
  1. Simple Object Destructuring

     const person = { name: 'Alice', age: 25 };
     const { name, age } = person;
    
     console.log(name); // Output: Alice
     console.log(age);  // Output: 25
    
  2. Renaming Variables

    • You can rename variables during destructuring using a colon (:).
    const person = { name: 'Alice', age: 25 };
    const { name: fullName, age: years } = person;

    console.log(fullName); // Output: Alice
    console.log(years);    // Output: 25
  1. Default Values

    • Provide default values for variables in case the property is undefined.
    const person = { name: 'Alice' };
    const { name, age = 30 } = person;

    console.log(name); // Output: Alice
    console.log(age);  // Output: 30
  1. Nested Destructuring

    • Destructure nested objects.
    const user = { name: 'Alice', address: { city: 'Wonderland', zip: '12345' } };
    const { name, address: { city, zip } } = user;

    console.log(name); // Output: Alice
    console.log(city); // Output: Wonderland
    console.log(zip);  // Output: 12345
  1. Function Parameters

    • Destructure parameters directly in function definitions.
    function greet({ name, age }) {
      console.log(`Hello, ${name}. You are ${age} years old.`);
    }

    const person = { name: 'Alice', age: 25 };
    greet(person); // Output: Hello, Alice. You are 25 years old.

Summary

  • Array Destructuring: Unpacks values from arrays into variables.

    • Syntax:const [a, b] = array;

    • Features: Skip values, use rest elements.

  • Object Destructuring: Unpacks properties from objects into variables.

    • Syntax:const { a, b } = object;

    • Features: Rename variables, provide default values, handle nested objects.

Destructuring assignment simplifies the process of extracting values from arrays and objects, making your code cleaner and more concise.

Rest and Spread Operators in JavaScript (ES6)

The rest and spread operators are two powerful features introduced in ES6 that help you work with arrays and objects more effectively. Although they look similar (both use ...), they serve different purposes.

Rest Operator (...)

The rest operator is used to collect multiple elements into an array. It’s useful in function parameters and destructuring assignments.

Syntax
function functionName(...restParameters) {
  // Function body
}

Examples of Rest Operator

  1. In Function Parameters

    • The rest operator collects all remaining arguments into an array.
    function printNames(firstName, ...otherNames) {
      console.log('First Name:', firstName);
      console.log('Other Names:', otherNames);
    }

    printNames('Alice', 'Bob', 'Charlie', 'David');

Output:

    First Name: Alice
    Other Names: ['Bob', 'Charlie', 'David']
  1. In Destructuring Assignments

    • You can use the rest operator to collect the remaining properties of an object or array.
    // Array Destructuring
    const [first, ...rest] = [1, 2, 3, 4];
    console.log(first); // 1
    console.log(rest);  // [2, 3, 4]

    // Object Destructuring
    const person = { name: 'Alice', age: 25, country: 'USA' };
    const { name, ...restInfo } = person;
    console.log(name);      // Alice
    console.log(restInfo); // { age: 25, country: 'USA' }

Spread Operator (...)

The spread operator is used to spread elements of an array or object into individual elements. It’s useful for combining arrays, copying arrays, and merging objects.

Syntax
const newArray = [...oldArray];
const newObject = { ...oldObject };

Examples of Spread Operator

  1. Combining Arrays

    • The spread operator can merge two or more arrays into one.
    const fruits = ['apple', 'banana'];
    const vegetables = ['carrot', 'broccoli'];
    const combined = [...fruits, ...vegetables];

    console.log(combined); // ['apple', 'banana', 'carrot', 'broccoli']
  1. Copying Arrays

    • Create a shallow copy of an array.
    const numbers = [1, 2, 3];
    const copy = [...numbers];

    console.log(copy); // [1, 2, 3]
  1. Merging Objects

    • The spread operator can merge properties from multiple objects.
    const user = { name: 'Alice', age: 25 };
    const contact = { email: 'alice@example.com', phone: '123-456-7890' };
    const userInfo = { ...user, ...contact };

    console.log(userInfo);
    // { name: 'Alice', age: 25, email: 'alice@example.com', phone: '123-456-7890' }
  1. Function Arguments

    • The spread operator can be used to pass elements of an array as arguments to a function.
    const numbers = [1, 2, 3];
    function add(a, b, c) {
      return a + b + c;
    }

    console.log(add(...numbers)); // 6

Summary

  • Rest Operator (...)

    • Purpose: Collect multiple elements into an array.

    • Usage: Function parameters, destructuring assignments.

  • Spread Operator (...)

    • Purpose: Spread elements of an array or object into individual elements.

    • Usage: Combining arrays, copying arrays, merging objects, passing arguments.

Both the rest and spread operators make working with arrays and objects more flexible and concise, simplifying code and improving readability.

Promises in JavaScript

A Promise is a JavaScript object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value. Promises make it easier to manage asynchronous code by allowing you to write cleaner, more understandable code compared to traditional callback methods.

Key Concepts of Promises

  1. Pending: The initial state of a Promise. The operation is still in progress, and the Promise is neither fulfilled nor rejected.

  2. Fulfilled: The state of a Promise when the operation completes successfully. It has a result or value.

  3. Rejected: The state of a Promise when the operation fails. It has an error or reason for the failure.

Creating a Promise

You create a Promise using the Promise constructor, which takes a function with two parameters: resolve and reject.

Syntax
const myPromise = new Promise((resolve, reject) => {
  // Asynchronous operation
});
  • resolve(value): Called when the operation completes successfully. The value is passed to the then handler.

  • reject(reason): Called when the operation fails. The reason (error) is passed to the catch handler.

Example of Creating and Using a Promise

const myPromise = new Promise((resolve, reject) => {
  let success = true; // Simulating success or failure
  if (success) {
    resolve('Operation successful!');
  } else {
    reject('Operation failed.');
  }
});

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

Chaining Promises

You can chain multiple .then handlers to handle a sequence of asynchronous operations. Each .then returns a new Promise, which allows for further chaining.

Example of Chaining Promises
const firstPromise = new Promise((resolve, reject) => {
  resolve('First operation completed.');
});

firstPromise
  .then(result => {
    console.log(result); // Output: First operation completed.
    return 'Second operation completed.';
  })
  .then(result => {
    console.log(result); // Output: Second operation completed.
  });

Handling Errors with Promises

Use .catch to handle errors in a Promise chain. It catches errors that occur in any of the preceding .then handlers.

Example of Error Handling
const myPromise = new Promise((resolve, reject) => {
  reject('Something went wrong!');
});

myPromise
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.log('Error:', error); // Output: Error: Something went wrong!
  });

Summary

  • Promise: Represents the result of an asynchronous operation. Can be in one of three states: pending, fulfilled, or rejected.

  • Creating a Promise: Use the Promise constructor with resolve and reject functions.

  • Using .then and .catch: Handle successful results and errors.

  • Chaining Promises: Allows sequential asynchronous operations.

  • Promise.all and Promise.race: Handle multiple Promises together, either waiting for all to complete or reacting to the first one.

Promises provide a powerful way to manage asynchronous operations, making your code more readable and easier to manage compared to callback-based approaches.

Did you find this article valuable?

Support Sarthak by becoming a sponsor. Any amount is appreciated!