March 26, 20266 min read

ES6+ Features That Actually Changed How We Write JavaScript

let/const, arrow functions, destructuring, spread/rest, template literals, modules, optional chaining, nullish coalescing — the features worth knowing cold.

javascript es6 modern javascript syntax fundamentals
Ad 336x280

ES6 (ES2015) landed over a decade ago, but plenty of developers still only use half of what it introduced. And features from ES2020+ like optional chaining and nullish coalescing have become just as essential. Here's the stuff that genuinely changed everyday JavaScript — no obscure corner cases, just the features you'll use in every codebase.

let and const

var is function-scoped and gets hoisted. let and const are block-scoped and don't (well, they're hoisted but sit in the "temporal dead zone" until declaration — the effect is the same: you can't use them before declaring them).
if (true) {
  var x = 1;
  let y = 2;
}
console.log(x); // 1 — var leaks out of blocks
console.log(y); // ReferenceError — let stays in the block

Use const by default. Switch to let when you need to reassign. Never use var. That's the convention almost everywhere now.

Note: const means the binding can't be reassigned. It does not mean the value is immutable:

const arr = [1, 2, 3];
arr.push(4);       // fine — mutating the array
arr = [5, 6, 7];   // TypeError — can't reassign the variable

Arrow functions

Shorter syntax, no own this binding:

// Old
const double = function (n) { return n * 2; };

// Arrow
const double = (n) => n * 2;

// Multi-line body needs braces and explicit return
const process = (data) => {
const cleaned = data.trim();
return cleaned.toUpperCase();
};

The this behavior is the real win. Arrow functions capture this from the surrounding scope, which eliminates the classic var self = this hack:

// Before arrows
Timer.prototype.start = function () {
  var self = this;
  setInterval(function () {
    self.tick(); // needed 'self' because 'this' was wrong
  }, 1000);
};

// With arrows
Timer.prototype.start = function () {
setInterval(() => {
this.tick(); // 'this' is correct automatically
}, 1000);
};

Template literals

Backtick strings with interpolation. Sounds minor, changed everything:

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

// Multi-line strings just work
const html =
<div class="card">
<h2>${title}</h2>
<p>${description}</p>
</div>
;

// Expressions inside interpolation
console.log(Total: $${(price * quantity).toFixed(2)});

No more 'Hello, ' + name + '!' string concatenation. No more \n everywhere.

Destructuring

Pull values out of objects and arrays in one shot:

// Object destructuring
const { name, age, email } = user;

// With rename
const { name: userName, email: userEmail } = user;

// With defaults
const { role = 'viewer' } = user;

// Array destructuring
const [first, second, ...rest] = [1, 2, 3, 4, 5];
// first = 1, second = 2, rest = [3, 4, 5]

// Skip elements
const [, , third] = ['a', 'b', 'c'];
// third = 'c'

In function parameters (extremely common in React):

function UserCard({ name, avatar, isOnline }) {
  // instead of: function UserCard(props) { const name = props.name; ... }
}

Spread and rest

Three dots, two uses. Spread expands, rest collects:

// Spread — expanding arrays/objects
const merged = [...array1, ...array2];
const copy = { ...original, name: 'updated' };  // shallow clone + override

// Rest — collecting remaining items
function log(first, ...others) {
console.log(first); // first argument
console.log(others); // array of the rest
}

Object spread for immutable updates is everywhere in React and Redux:

const updated = { ...state, loading: false, data: newData };

Default parameters

function greet(name = 'stranger', punctuation = '!') {
  return Hello, ${name}${punctuation};
}

greet(); // "Hello, stranger!"
greet('Sam'); // "Hello, Sam!"
greet('Sam', '.'); // "Hello, Sam."

Replaces the old name = name || 'stranger' pattern, which incorrectly treated '', 0, and false as missing.

Modules (import/export)

// math.js
export function add(a, b) { return a + b; }
export const PI = 3.14159;
export default class Calculator { / ... / }

// app.js
import Calculator, { add, PI } from './math.js';

Named exports for most things, default export for the primary thing a module provides. This is how every modern JS project is organized.

Map and Set

Map is like an object but keys can be anything (not just strings), and it maintains insertion order:
const cache = new Map();
cache.set(userObj, expensiveResult);  // object as key
cache.has(userObj);  // true
cache.size;          // number of entries
Set is a collection of unique values:
const unique = new Set([1, 2, 2, 3, 3, 3]);
// Set { 1, 2, 3 }
unique.add(4);
unique.has(2);  // true
[...unique];    // [1, 2, 3, 4] — back to array

Quick array dedup: [...new Set(array)].

Optional chaining (?.)

Safely access nested properties without checking every level:

// Before
const street = user && user.address && user.address.street;

// After
const street = user?.address?.street;

// Works with methods and bracket notation too
const result = api?.getData?.();
const val = obj?.[dynamicKey];

Returns undefined if any part of the chain is null or undefined. Eliminated an entire category of verbose null checks.

Nullish coalescing (??)

Provides a default only when the value is null or undefined — not for 0, '', or false:

const port = config.port ?? 3000;
// If config.port is 0, you get 0 (correct)
// With ||, you'd get 3000 (wrong — 0 is falsy)

const name = input ?? 'Anonymous';

?? with ?. is a powerful combo:
const theme = user?.preferences?.theme ?? 'light';

for...of

Iterates over values (arrays, strings, Maps, Sets, generators):

for (const item of items) {
  console.log(item);  // the value, not the index
}

// With entries() if you need the index
for (const [i, item] of items.entries()) {
console.log(i, item);
}

Cleaner than forEach (you can break and continue), clearer than for...in (which iterates over keys and includes prototype properties).


These features aren't "new" anymore — they're baseline modern JavaScript. If any of them aren't second nature yet, it's worth the time to practice until they are. Try the interactive JavaScript exercises at CodeUp to drill these patterns until they're muscle memory.

Ad 728x90