No history yet

Modern ES6+ Syntax

Beyond `var`

You're likely familiar with declaring variables using var. For a long time, it was the only way. But var has some tricky behaviors, like being scoped to functions rather than code blocks, which can lead to unexpected bugs.

ES6 introduced let and const to provide a more predictable way to declare variables. Both are block-scoped, meaning they only exist within the nearest set of curly braces ({...}), like an if statement or a for loop.

if (true) {
  var a = 1; // Exists outside this block
  let b = 2; // Only exists in this block
  const c = 3; // Only exists in this block
}

console.log(a); // 1
// console.log(b); // Throws ReferenceError: b is not defined
// console.log(c); // Throws ReferenceError: c is not defined

The key difference between let and const is re-assignment.

  • let allows you to reassign the variable's value later.
  • const (short for constant) declares a variable that cannot be reassigned. This helps prevent accidental changes to important values.

It's a common practice to default to const and only use let when you know a variable's value will need to change.

Note that const doesn't make objects or arrays immutable. You can still change their contents (e.g., add an item to an array), but you can't reassign the variable to a completely new object or array.

Smarter Strings and Functions

String concatenation in JavaScript used to be clumsy, involving lots of + signs. Template literals clean this up dramatically. By using backticks (`) instead of quotes, you can embed expressions directly into strings and create multi-line strings without special characters.

const user = {
  name: 'Alex',
  plan: 'Pro'
};

// Old way
const welcomeMessageOld = 'Welcome, ' + user.name + '!\n' + 
                          'Your current plan is ' + user.plan + '.';

// New way with template literals
const welcomeMessageNew = `Welcome, ${user.name}!
Your current plan is ${user.plan}.`;

console.log(welcomeMessageNew);
// Welcome, Alex!
// Your current plan is Pro.

Another major syntax improvement came with arrow functions. They provide a shorter syntax for writing functions and, crucially, they don't have their own this context. Instead, they inherit this from the surrounding (lexical) scope.

// Traditional function
function double(x) {
  return x * 2;
}

// Arrow function
const doubleArrow = x => x * 2;

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

// Arrow function with a code block
const greet = name => {
  const message = `Hello, ${name}`;
  return message;
};

The lexical this behavior is particularly useful in event handlers or callbacks inside objects, where traditional functions often forced you to use workarounds like that = this or .bind(this) to maintain the correct context.

Unpacking Data

Destructuring is a convenient way to extract data from arrays or objects into distinct variables. It makes your code cleaner and easier to read by avoiding repetitive property access.

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

// Object destructuring
const person = {
  firstName: 'Jane',
  age: 30,
  city: 'New York'
};

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

You can also rename variables and provide default values during destructuring, which is helpful when dealing with optional properties.

const settings = {
  theme: 'dark'
};

// Rename 'theme' to 'colorTheme' and provide a default for 'fontSize'
const { theme: colorTheme, fontSize = 16 } = settings;

console.log(colorTheme); // 'dark'
console.log(fontSize); // 16

Spread and Rest

The three-dot syntax (...) serves two opposite purposes: the spread operator and the rest parameter. The context determines which one it is.

The spread operator "spreads out" the elements of an array or the properties of an object. It's great for creating copies or combining data structures without modifying the originals.

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

// Spread with objects
const userDetails = {
  name: 'Sam',
  id: 123
};
const userPermissions = {
  isAdmin: false
};

const fullUser = { ...userDetails, ...userPermissions };
// { name: 'Sam', id: 123, isAdmin: false }

The rest parameter does the opposite. It collects multiple elements or arguments into a single array. This is useful for functions that can accept a variable number of arguments, or in destructuring to gather the remaining items.

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

sum(1, 2, 3); // 6
sum(10, 20); // 30

// Rest in array destructuring
const [first, second, ...others] = [10, 20, 30, 40, 50];
console.log(first); // 10
console.log(second); // 20
console.log(others); // [30, 40, 50]

Finally, ES6 introduced enhanced object literals, which provide shorthand for initializing properties from variables and defining methods.

const name = 'Laptop';
const price = 1200;

const product = {
  // Shorthand for name: name
  name,
  // Shorthand for price: price
  price,
  // Shorthand method definition
  display() {
    console.log(`${this.name} costs 💲${this.price}`);
  }
};

product.display(); // Laptop costs 💲1200

These modern syntax features help you write code that is more concise, readable, and less prone to common errors. As you continue your journey, you'll see them used everywhere in modern JavaScript codebases.