Modern JavaScript Syntax

Object Destructuring

Object destructuring allows you to extract properties from an object by their names. The variable names must match the property keys.

const user = {
  id: 42,
  firstName: 'John',
  lastName: 'Doe',
  email: 'john.doe@example.com'
};

// Extracting properties into variables
const { firstName, email } = user;

console.log(firstName); // "John"
console.log(email);     // "john.doe@example.com"

Renaming Variables

If you want to create a variable with a different name than the object's property, you can use a colon (:).

const { firstName: name, email: userEmail } = user;

console.log(name);      // "John"
console.log(userEmail); // "john.doe@example.com"

Default Values

You can provide a default value for a property in case it doesn't exist in the object.

const settings = {
  theme: 'dark',
  fontSize: 14
};

const { theme, fontSize, layout = 'grid' } = settings;

console.log(theme);   // "dark"
console.log(layout);  // "grid" (since it wasn't in the object)

Array Destructuring

Array destructuring allows you to unpack values from an array based on their position (index).

const colors = ['Red', 'Green', 'Blue'];

// Extracting values by position
const [firstColor, secondColor] = colors;

console.log(firstColor);  // "Red"
console.log(secondColor); // "Green"

Skipping Elements

You can skip elements in an array by using a comma without a variable name.

const rgb = [255, 128, 0];

const [red, , blue] = rgb;

console.log(red);  // 255
console.log(blue); // 0

The Rest Pattern

You can use the rest syntax (...) to collect all remaining elements into a new array.

const players = ['Alice', 'Bob', 'Charlie', 'David'];

const [winner, ...restOfPlayers] = players;

console.log(winner);          // "Alice"
console.log(restOfPlayers); // ["Bob", "Charlie", "David"]

Practical Use Cases

Destructuring is incredibly useful, especially when working with functions.

Function Parameters

It's common to pass a configuration object to a function. Destructuring in the parameter list makes the code much cleaner.

// Instead of this:
function createUser(options) {
  const name = options.name;
  const age = options.age;
  // ...
}

// You can do this:
function createUser({ name, age, email = 'default@email.com' }) {
  console.log(`Creating user ${name}, age ${age} with email ${email}`);
}

createUser({ name: 'Jane', age: 30 });
// Output: Creating user Jane, age 30 with email default@email.com

Swapping Variables

Destructuring provides an elegant one-liner for swapping the values of two variables without needing a temporary variable.

let a = 10;
let b = 20;

[a, b] = [b, a];

console.log(a); // 20
console.log(b); // 10

Template literals

Template literals are an enhanced way to create strings in JavaScript. Instead of single (') or double (") quotes, you use backticks (` ) to enclose your string. This allows you to easily embed variables and create multi-line strings.

Variables and Expressions Inside Strings

Template literals allow you to embed variables or even full JavaScript expressions directly into a string using the ${expression} syntax. This is called interpolation. It's much cleaner than traditional string concatenation using the + operator.

Before: The Old Way (Concatenation)

const name = 'Alice';
const score = 98;

const message = 'Hello, ' + name + '! Your score is ' + score + '.';
console.log(message); // "Hello, Alice! Your score is 98."

After: With Template Literals

const name = 'Alice';
const score = 98;

const message = `Hello, ${name}! Your score is ${score}.`;
console.log(message); // "Hello, Alice! Your score is 98."

As you can see, the code is far more readable and less prone to errors from missing spaces or quotes.

Multi-line Strings

Creating strings that span multiple lines is effortless with template literals. Any line breaks you add inside the backticks are preserved in the final string.

Before: The Old Way (\n)

const address = '123 Maple Street\n' +
                'Anytown, USA 12345';

console.log(address);
// 123 Maple Street
// Anytown, USA 12345

After: With Template Literals

const address = `123 Maple Street
Anytown, USA 12345`;

console.log(address);
// 123 Maple Street
// Anytown, USA 12345

This method is much more intuitive, as the string in your code looks exactly like the final output.

Spread and Rest Operators:

The spread (...) and rest (...) operators in JavaScript use the same three-dot syntax but perform opposite actions. The spread operator expands an iterable (like an array or object) into individual elements, while the rest operator collects multiple elements into a single array.

The Spread Operator (...)

The spread operator unpacks elements. Think of it as taking a container and spreading its contents out on a table. It's commonly used for combining, copying, or passing elements from an iterable.

Practical Examples:

  1. Combining Arrays and Objects:

     const fruits = ['apple', 'Banana'];
     const vegetables = ['carrot', Broccoli'];
    
     const food = [...fruits, ...vegetables];
     console.log(food); // ['apple', 'Banana', 'carrot', 'Broccoli']
    
     const user = { name: 'Alex', age: 30 };
     const details = { city: 'New York', job: 'Developer' };
    
     const userProfile = { ...user, ...details };
     console.log(userProfile); // { name: 'Alex', age: 30, city: 'New York', job: 'Developer' }
    
  2. Passing Arguments to a Function:

    You can spread an array's elements as individual arguments into a function.

     const numbers = [10, 5, 25, 15];
    
     // Instead of Math.max(10, 5, 25, 15)
     const maxNumber = Math.max(...numbers);
     console.log(maxNumber); // 25
    

    Spread Operator: Shallow Copy vs. Deep Copy

    It's crucial to understand that the spread operator performs a shallow copy, not a deep copy.

    • Shallow Copy: This means it copies the top-level properties. However, if a property is a nested object or array, it only copies the reference (the memory address) to that nested structure, not the structure itself. Modifying the nested object in the copy will also affect the original.

    • Deep Copy: This creates a completely independent copy of the original object, including all nested structures.

Example of a Shallow Copy

Notice how changing the nested details object in the copy also changes it in the original.

    const original = {
      name: 'Alice',
      details: { age: 30, city: 'London' }
    };

    // Using spread to create a shallow copy
    const copy = { ...original };

    // Modifying a top-level property only affects the copy
    copy.name = 'Bob';

    // Modifying a nested property affects BOTH objects ๐Ÿ˜ฑ
    copy.details.age = 31;

    console.log(original.name);       // "Alice" (Unaffected)
    console.log(copy.name);           // "Bob" (Changed)

    console.log(original.details.age); // 31 (Affected!)
    console.log(copy.details.age);     // 31 (Changed)

This happens because both original.details and copy.details point to the exact same object in memory. To create a deep copy, you would need other methods like JSON.parse(JSON.stringify(obj)) for simple cases, or a dedicated library like Lodash for more complex scenarios.

The Rest Operator (...) ๐Ÿ“ฆ

The rest operator collects elements. It gathers an indefinite number of elements into a single array. This is incredibly useful in function parameters and destructuring.

Practical Examples:

  1. Collecting Function Arguments: You can create functions that accept any number of arguments.

     function sum(...numbers) {
       return numbers.reduce((total, num) => total + num, 0);
     }
    
     console.log(sum(1, 2));         // 3
     console.log(sum(5, 10, 15));    // 30
     console.log(sum(2, 4, 6, 8, 10)); // 30
    
  2. Destructuring Arrays: You can use the rest operator to collect the "rest" of the elements from an array during destructuring.

     const players = ['Alice', 'Bob', 'Charlie', 'David'];
    
     const [captain, coCaptain, ...team] = players;
    
     console.log(captain);    // 'Alice'
     console.log(coCaptain);  // 'Bob'
     console.log(team);       // ['Charlie', 'David']
    

Now since both spread and Rest opearator look alike it will be difficult for freshers to indentify them :

The simplest rule of thumb is:

  • Spread (...) Unpacks: When you see ... being used to create something new (like in an array or object literal) or in a function call, it's spreading values out of an iterable.

  • Rest (...) Collects: When you see ... in a function definition's parameters or on the left side of a destructuring assignment, it's collecting values into an array.

Comparison:

1. In Arrays & Destructuring

Spread Operator:

Used on the "right side" of an assignment to create a new array.

 const arr1 = [1, 2]; 
 const combined = [...arr1, 3, 4]; 
// Unpacks arr1 console.log(combined); // [1, 2, 3, 4]

Rest Operator:

Used on the "left side" of a destructuring assignment.

const numbers = [1, 2, 3, 4]; 
const [first, ...others] = numbers; 
// Collects the rest console.log(first); 
// 1 console.log(others); // [2, 3, 4]

Here, ...arr1 is spreading its values into a new array literal. In the other example, ...others is a variable being declared to collect the rest of the values.

2. In Functions:

Spread Operator (In a Function Call)

Used when you call a function to pass an array's elements as individual arguments.

const numbers = [5, 10, 15]; 
// Spreads `numbers` into individual arguments 
function add(a, b, c) { 
    return a + b + c; 
} 
console.log(add(...numbers)); // 30

Rest Operator (In a Function's Parameters):

Used when you define a function to gather an indefinite number of arguments into an array.

// Collects all arguments into the `numbers` array
 function sum(...numbers) { 
    return numbers.reduce((acc, n) => acc + n, 0); 
} 
console.log(sum(5, 10, 15)); // 30

Here, add(...numbers) is spreading the array into arguments 5, 10, 15. In the other example, the sum function's parameter ...numbers is collecting all passed arguments into a single array.

Working with Map and Set:

Map and Set are modern JavaScript data structures that offer more specialized and powerful ways to organize data compared to traditional objects and arrays. A Map is a collection of key-value pairs where keys can be of any data type, and a Set is a collection of unique values.

Map: For Key-Value Pairs:

A Map is similar to a plain JavaScript Object, but it has one major advantage: keys can be any data type (including objects, functions, or numbers), not just strings. Maps also maintain the insertion order of their elements.

Key Methods:

  • set(key, value): Adds a new key-value pair.

  • get(key): Retrieves the value for a given key.

  • has(key): Checks if a key exists.

  • delete(key): Removes a key-value pair.

  • size: A property that returns the number of entries.

  • clear(): Removes all entries.

Example Usage:

const user1 = { name: 'Alice' };
const user2 = { name: 'Bob' };

const userStatus = new Map();

userStatus.set(user1 , 'Online');
userStatus.set(user2 , 'Offline');

console.log(userStatus.get(user1)); //output: Online
console.log(userStatus.get(user2));//output : Offline

//orignal object remain unchanged no status prperty was added:
console.log(user1) //output : {name: 'Alice'}

This keeps data clean:

Notice that the original objects are unchanged. The status information is stored separately in our Map.

Objects as Keys: The most important feature of Map is that we used the actual user1 and user2 objects as the keys. A regular {} object can't do this properly.

Set: For Unique Values

A Set is a collection where each value must be unique. If you try to add a value that already exists, the Set will simply ignore it. This makes Set perfect for tasks like removing duplicate elements from an array. Like Map, Set also maintains insertion order.

Key Methods:

  • add(value): Adds a new value.

  • has(value): Checks if a value exists.

  • delete(value): Removes a value.

  • size: A property that returns the number of values.

  • clear(): Removes all values.

Example Usage

// Create a new Set
const tags = new Set();

// Use .add() to add values
tags.add('JavaScript');
tags.add('WebDev');
tags.add('Programming');

// Adding a duplicate value does nothing
tags.add('JavaScript'); 

console.log(tags); // Set(3) { 'JavaScript', 'WebDev', 'Programming' }
console.log(tags.size); // 3
console.log(tags.has('WebDev')); // true

tags.delete('Programming');
console.log(tags.has('Programming')); // false

Practical Use Case: Removing Duplicates from an Array

This is the most common use case for Set.

const numbers = [1, 5, 2, 5, 3, 1, 4, 2];

const uniqueNumbers = [...new Set(numbers)];

console.log(uniqueNumbers); // [1, 5, 2, 3, 4]
0
Subscribe to my newsletter

Read articles from Syed Wasif Hussain directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Syed Wasif Hussain
Syed Wasif Hussain