Skip to main content
ES2015 (ES6) and later versions introduced a large set of features that are now standard in every codebase. These come up frequently in interviews.

Arrow functions

// Regular function — has its own `this`
function add(a, b) { return a + b; }

// Arrow function — inherits `this` from enclosing scope
const add = (a, b) => a + b;

// Gotcha: arrow functions cannot be used as constructors
// and don't have their own `arguments` object
const obj = {
  name: "Alice",
  greet: function() { console.log(this.name); },  // works
  greetArrow: () => console.log(this.name),        // `this` is outer scope (window/undefined)
};

Destructuring

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

// Object destructuring
const { name, age, city = "Unknown" } = user;

// Rename while destructuring
const { name: userName } = user;

// Nested
const { address: { street } } = user;

// In function parameters
function greet({ name, age }) {
  return `${name} is ${age}`;
}

Spread & rest

// Spread — expand an iterable
const arr = [1, 2, 3];
const copy = [...arr];
const merged = [...arr, 4, 5];

const obj = { a: 1, b: 2 };
const extended = { ...obj, c: 3 };

// Rest — collect remaining args into array
function sum(...numbers) {
  return numbers.reduce((acc, n) => acc + n, 0);
}

Template literals

const name = "World";
const greeting = `Hello, ${name}!`;

// Multi-line
const html = `
  <div>
    <p>${greeting}</p>
  </div>
`;

// Tagged templates
function highlight(strings, ...values) {
  return strings.reduce((acc, str, i) =>
    acc + str + (values[i] ? `<b>${values[i]}</b>` : ""), "");
}

Optional chaining & nullish coalescing

// Optional chaining — short-circuits to undefined instead of throwing
const city = user?.address?.city;
const fn = obj?.method?.();

// Nullish coalescing — fallback only for null/undefined (not 0 or "")
const name = user.name ?? "Anonymous";

// vs OR — fallback for any falsy value (0, "", false also trigger fallback)
const name2 = user.name || "Anonymous";

Modules

// Named exports
export const PI = 3.14;
export function add(a, b) { return a + b; }

// Default export
export default class Calculator { ... }

// Imports
import Calculator from "./calculator";
import { PI, add } from "./math";
import * as math from "./math";
import { add as sum } from "./math"; // alias

Other key features

FeatureExample
let/constBlock-scoped variable declarations
SymbolUnique primitive values
Map/SetKey-value and unique-value collections
WeakMap/WeakSetGarbage-collectible references
Proxy/ReflectIntercept object operations
Generator functionsfunction*, yield
for...ofIterate over iterables
PromiseBuilt-in async abstraction
classSyntactic sugar for prototypal inheritance

Common interview questions

They use the same syntax (...) but in different contexts:
  • Spread — used in call/literal position; expands an iterable into individual elements.
  • Rest — used in parameter/destructuring position; collects multiple elements into an array.
  • Keys can be any type (not just strings/Symbols).
  • Maintains insertion order reliably.
  • Has a built-in size property.
  • Better performance for frequent add/delete operations.
  • No inherited prototype keys that could conflict.
|| returns the right-hand side when the left is falsy (0, "", false, null, undefined). ?? (nullish coalescing) only returns the right-hand side when the left is null or undefined, so 0 and "" are treated as valid values.