Advanced JavaScript

Master advanced JavaScript concepts including design patterns, performance optimization, and modern development practices.

advanced JavaScript 7 hours

Chapter 12: Advanced ES6+ Features

Chapter 12 of 15

Chapter 12: Advanced ES6+ Features

12.1 Symbols and Iterators

Symbols are unique, immutable primitive values used as object property keys. They provide a way to create private properties and avoid naming conflicts.

// Creating symbols
const sym1 = Symbol();
const sym2 = Symbol('description');
const sym3 = Symbol('description');

console.log(sym2 === sym3); // false - each Symbol is unique

// Using symbols as property keys
const obj = {
    [sym1]: 'value1',
    [sym2]: 'value2',
    regularKey: 'value3'
};

// Symbols are not enumerable
Object.keys(obj); // ['regularKey']
Object.getOwnPropertySymbols(obj); // [Symbol(), Symbol('description')]

Well-Known Symbols:

// Symbol.iterator - Makes objects iterable
const iterable = {
    [Symbol.iterator]: function* () {
        yield 1;
        yield 2;
        yield 3;
    }
};

for (const value of iterable) {
    console.log(value); // 1, 2, 3
}

// Symbol.toStringTag - Custom toString behavior
class MyClass {
    get [Symbol.toStringTag]() {
        return 'MyClass';
    }
}
console.log(new MyClass().toString()); // "[object MyClass]"

12.2 Generators

// Generator function
function* numberGenerator() {
    yield 1;
    yield 2;
    yield 3;
}

const gen = numberGenerator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }

// Infinite generator
function* infiniteNumbers() {
    let n = 0;
    while (true) {
        yield n++;
    }
}

const numbers = infiniteNumbers();
console.log(numbers.next().value); // 0
console.log(numbers.next().value); // 1

12.3 Proxies

// Proxy - Intercept object operations
const target = { name: 'John', age: 30 };

const handler = {
    get: function(obj, prop) {
        if (prop === 'age') {
            return 'Age is private';
        }
        return obj[prop];
    },
    set: function(obj, prop, value) {
        if (prop === 'age' && value < 0) {
            throw new Error('Age cannot be negative');
        }
        obj[prop] = value;
        return true;
    }
};

const proxy = new Proxy(target, handler);
console.log(proxy.name); // "John"
console.log(proxy.age);  // "Age is private"
proxy.age = 25; // Works
proxy.age = -5; // Throws error

12.4 Reflect API

const obj = { name: 'John' };

// Reflect provides methods for object operations
Reflect.get(obj, 'name'); // "John"
Reflect.set(obj, 'age', 30);
Reflect.has(obj, 'name'); // true
Reflect.deleteProperty(obj, 'age');
Reflect.ownKeys(obj); // ['name']