JavaScript
05 / 07

ES6+ Features & Syntax

Modern JavaScript ES6+ Features

ES6 (ES2015) and later versions introduced significant improvements to JavaScript. Understanding these features is essential for modern JavaScript development.

Let, Const, and Block Scope

// var - function scoped, hoisted
var x = 1;
if (true) {
  var x = 2;  // Same variable
  console.log(x);  // 2
}
console.log(x);  // 2

// let - block scoped, not hoisted
let y = 1;
if (true) {
  let y = 2;  // Different variable
  console.log(y);  // 2
}
console.log(y);  // 1

// const - block scoped, cannot be reassigned
const z = 1;
// z = 2;  // Error!

// const with objects/arrays - reference is constant, not content
const user = { name: 'John' };
user.name = 'Jane';  // ✅ OK - modifying property
user.age = 30;       // ✅ OK - adding property
// user = {};        // ❌ Error - cannot reassign

const numbers = [1, 2, 3];
numbers.push(4);     // ✅ OK - modifying array
// numbers = [];     // ❌ Error - cannot reassign

Arrow Functions

// Traditional function
function add(a, b) {
  return a + b;
}

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

// Single parameter - parentheses optional
const square = x => x * x;

// No parameters - parentheses required
const greet = () => console.log('Hello');

// Multiple statements - need braces and return
const calculate = (a, b) => {
  const sum = a + b;
  const product = a * b;
  return { sum, product };
};

// Lexical 'this' binding
function Person() {
  this.age = 0;
  
  // Traditional function - 'this' refers to global object
  setInterval(function() {
    this.age++;  // Won't work as expected
  }, 1000);
  
  // Arrow function - 'this' refers to Person
  setInterval(() => {
    this.age++;  // ✅ Works correctly
  }, 1000);
}

// Array methods with arrows
const numbers = [1, 2, 3, 4, 5];

const doubled = numbers.map(n => n * 2);
const evens = numbers.filter(n => n % 2 === 0);
const sum = numbers.reduce((acc, n) => acc + n, 0);
const hasThree = numbers.some(n => n === 3);
const allPositive = numbers.every(n => n > 0);

Destructuring

// Array destructuring
const [first, second, third] = [1, 2, 3];
console.log(first);  // 1

// Skip elements
const [a, , c] = [1, 2, 3];
console.log(a, c);  // 1, 3

// Rest operator
const [head, ...tail] = [1, 2, 3, 4, 5];
console.log(head);  // 1
console.log(tail);  // [2, 3, 4, 5]

// Default values
const [x = 0, y = 0] = [10];
console.log(x, y);  // 10, 0

// Object destructuring
const user = { name: 'John', age: 30, email: 'john@example.com' };
const { name, age } = user;
console.log(name, age);  // 'John', 30

// Rename variables
const { name: userName, age: userAge } = user;
console.log(userName);  // 'John'

// Default values
const { name, age, country = 'USA' } = user;
console.log(country);  // 'USA'

// Nested destructuring
const data = {
  user: {
    name: 'John',
    address: {
      city: 'NYC',
      zip: '10001'
    }
  }
};

const { user: { name, address: { city } } } = data;
console.log(name, city);  // 'John', 'NYC'

// Function parameter destructuring
function greet({ name, age }) {
  console.log(`Hello ${name}, you are ${age}`);
}

greet(user);  // Hello John, you are 30

Spread & Rest Operators

// Spread operator - expands iterables

// Arrays
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];  // [1,2,3,4,5,6]

// Clone array
const original = [1, 2, 3];
const copy = [...original];

// Add to array
const withExtra = [...arr1, 4, 5];

// Objects
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const merged = { ...obj1, ...obj2 };  // { a: 1, b: 2, c: 3, d: 4 }

// Clone object
const person = { name: 'John', age: 30 };
const clone = { ...person };

// Override properties
const updated = { ...person, age: 31 };

// Rest operator - collects arguments
function sum(...numbers) {
  return numbers.reduce((acc, n) => acc + n, 0);
}

console.log(sum(1, 2, 3, 4));  // 10

// With other parameters
function multiply(multiplier, ...numbers) {
  return numbers.map(n => n * multiplier);
}

console.log(multiply(2, 1, 2, 3));  // [2, 4, 6]

Template Literals

// String interpolation
const name = 'John';
const age = 30;
const message = `Hello, ${name}! You are ${age} years old.`;

// Multiline strings
const html = `
  <div>
    <h1>${name}</h1>
    <p>Age: ${age}</p>
  </div>
`;

// Expressions in templates
const result = `Sum: ${2 + 3}`;
const price = 19.99;
const tax = `Total: $${(price * 1.1).toFixed(2)}`;

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

const highlighted = highlight`Hello ${name}, you are ${age} years old`;
// 'Hello <mark>John</mark>, you are <mark>30</mark> years old'

Enhanced Object Literals

// Property shorthand
const name = 'John';
const age = 30;

// Old way
const person1 = { name: name, age: age };

// ES6 way
const person2 = { name, age };

// Method shorthand
const obj = {
  // Old way
  greet: function() {
    console.log('Hello');
  },
  
  // ES6 way
  greet() {
    console.log('Hello');
  },
  
  // Async method
  async fetchData() {
    const data = await fetch('/api/data');
    return data.json();
  }
};

// Computed property names
const propName = 'score';
const game = {
  [propName]: 100,
  [`${propName}Max`]: 1000,
  ['player' + 1]: 'John'
};
console.log(game);  // { score: 100, scoreMax: 1000, player1: 'John' }

Classes

// ES6 Classes
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  
  greet() {
    console.log(`Hello, I'm ${this.name}`);
  }
  
  // Static method
  static create(name, age) {
    return new Person(name, age);
  }
  
  // Getter
  get info() {
    return `${this.name} (${this.age})`;
  }
  
  // Setter
  set info(value) {
    const [name, age] = value.split(' ');
    this.name = name;
    this.age = parseInt(age);
  }
}

const john = new Person('John', 30);
john.greet();  // Hello, I'm John
console.log(john.info);  // John (30)

// Inheritance
class Employee extends Person {
  constructor(name, age, jobTitle) {
    super(name, age);  // Call parent constructor
    this.jobTitle = jobTitle;
  }
  
  greet() {
    super.greet();  // Call parent method
    console.log(`I work as a ${this.jobTitle}`);
  }
}

const emp = new Employee('Jane', 28, 'Developer');
emp.greet();
// Hello, I'm Jane
// I work as a Developer

Modules (Import/Export)

// utils.js - Named exports
export const PI = 3.14159;
export function add(a, b) {
  return a + b;
}
export class Calculator {
  // ...
}

// Or export all at once
const PI = 3.14159;
function add(a, b) {
  return a + b;
}
export { PI, add };

// Default export (one per file)
export default function multiply(a, b) {
  return a * b;
}

// Or
function multiply(a, b) {
  return a * b;
}
export default multiply;

// Importing
import multiply from './utils.js';  // Default import
import { PI, add } from './utils.js';  // Named imports
import multiply, { PI, add } from './utils.js';  // Both
import * as utils from './utils.js';  // All as namespace

// Rename imports
import { add as sum } from './utils.js';

// Re-exporting
export { add } from './utils.js';
export * from './utils.js';

Default Parameters & Rest/Spread

// Default parameters
function greet(name = 'Guest', greeting = 'Hello') {
  return `${greeting}, ${name}!`;
}

console.log(greet());  // 'Hello, Guest!'
console.log(greet('John'));  // 'Hello, John!'
console.log(greet('John', 'Hi'));  // 'Hi, John!'

// Computed default values
function createUser(name, id = Date.now()) {
  return { name, id };
}

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

console.log(sum(1, 2, 3, 4, 5));  // 15

// Spread in function calls
const nums = [1, 2, 3];
console.log(Math.max(...nums));  // 3

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free