No history yet

Modern ES6+ Syntax

Writing Modern JavaScript

JavaScript has evolved significantly since its early days. The introduction of ECMAScript 2015 (often called ES6) marked a major turning point, bringing features that make the language safer, more powerful, and much more pleasant to write. Let's explore how these modern syntax additions can clean up your code.

Rethinking Variables with let and const

For years, var was the only way to declare a variable. Its function-scoped nature, however, could lead to confusing behavior, especially in loops and conditional blocks. Modern JavaScript introduced let and const to provide block-level scoping. This means a variable declared with let or const only exists within the nearest set of curly braces {}.

Variables declared with let can be reassigned, while variables declared with const cannot. Use const by default for predictability, and switch to let only when you know a variable's value needs to change.

// Old 'var' behavior
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log('var i:', i), 10); // Logs 3, 3, 3
}

// Modern 'let' behavior
for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log('let j:', j), 10); // Logs 0, 1, 2
}

const PI = 3.14159;
// PI = 3; // This would throw an error

The var loop logs 3 three times because the variable i is function-scoped and its final value is 3 when the setTimeout callbacks eventually run. In contrast, let creates a new binding for j in each loop iteration, preserving the value as you'd expect.

Cleaner Functions and Strings

Two of the most frequently used modernizations are arrow functions and template literals. Arrow functions offer a more concise syntax for writing functions and, more importantly, handle the this keyword differently than traditional functions.

// Traditional function expression
const square = function(x) {
  return x * x;
};

// Arrow function expression
const squareArrow = x => x * x;

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

// Arrow function with a multi-line body
const getUser = id => {
  // some logic to fetch a user
  return { id: id, name: 'Alex' };
};

The key benefit of arrow functions is their handling of this. They don't have their own this context; instead, they inherit it from the surrounding code. This behavior is called . It solves a common source of bugs in older JavaScript, especially within event handlers or methods that use callbacks.

Template literals make string construction much cleaner. Instead of clumsy concatenation with the + operator, you can embed expressions directly into a string using backticks () and ${}`.

const user = { name: 'Jordan', items: 3 };

// Old way
const messageOld = 'Hello, ' + user.name + '!\nYou have ' + user.items + ' items in your cart.';

// With template literals
const messageNew = `Hello, ${user.name}!
You have ${user.items} items in your cart.`;

console.log(messageNew);
// Output:
// Hello, Jordan!
// You have 3 items in your cart.

Unpacking Data with Ease

Modern applications often deal with complex data structures. Destructuring provides an elegant way to extract values from arrays and objects and assign them to variables. Similarly, the spread (...) and rest (...) operators provide powerful shortcuts for working with collections of data.

Destructuring pulls elements out. The spread operator pushes elements in. The rest operator collects remaining elements into a new array.

Let's see destructuring in action.

// Object destructuring
const person = {
  firstName: 'Sarah',
  lastName: 'Connor',
  age: 35
};

const { firstName, age } = person;
console.log(firstName); // 'Sarah'
console.log(age); // 35

// Array destructuring
const coordinates = [10, 20, 30];
const [x, y] = coordinates;
console.log(x); // 10
console.log(y); // 20

The spread and rest operators use the same ... syntax but for different purposes. The spread operator expands an iterable (like an array or object) into its individual elements. It's great for combining arrays or creating copies.

// Spread Operator
const firstHalf = ['a', 'b'];
const secondHalf = ['c', 'd'];

const combined = [...firstHalf, ...secondHalf]; // ['a', 'b', 'c', 'd']

const user = { name: 'Kyle', role: 'admin' };
const userWithId = { ...user, id: 123 }; // { name: 'Kyle', role: 'admin', id: 123 }

The rest operator does the opposite: it collects multiple elements into a single array. This is especially useful in function definitions to handle a variable number of arguments.

// Rest Operator
function sum(...numbers) {
  return numbers.reduce((total, num) => total + num, 0);
}

console.log(sum(1, 2, 3)); // 6
console.log(sum(10, 20, 30, 40)); // 100

const [first, ...rest] = [10, 20, 30, 40];
console.log(first); // 10
console.log(rest); // [20, 30, 40]

Finally, enhanced object literals provide shorthand for initializing object properties and methods. If your variable name is the same as your object key, you can just list the variable.

const name = 'Gadget';
const price = 99.99;

// Old way
const itemOld = {
  name: name,
  price: price,
  display: function() {
    console.log(this.name + ' costs ' + this.price);
  }
};

// Enhanced object literal
const itemNew = {
  name,
  price,
  display() {
    console.log(`${this.name} costs ${this.price}`);
  }
};

itemNew.display(); // Gadget costs 99.99
Quiz Questions 1/6

What is the primary difference between variables declared with let and const versus those declared with var?

Quiz Questions 2/6

Which of the following correctly uses a template literal to create a string?

Adopting these modern features leads to code that is not only shorter but also more expressive and less prone to common errors.