DNA⚡ JavaScriptES Modules, Proxy, Generators & Modern JavaScript
ðŸĢHatchlingJavaScriptES ModulesModern JS

ES Modules, Proxy, Generators & Modern JavaScript

Beyond ES6 basics. The modern JavaScript features that power frameworks, tooling, and advanced patterns — from modules to metaprogramming.

ES Modules, Proxy, Generators & Modern JavaScript

Senior engineers need to understand the features that power modern tooling and frameworks. ES modules are the foundation of tree shaking. Proxies power Vue's reactivity and MobX. Generators power Redux-Saga and async iteration. These aren't trivia — they're architecture.

ES Modules

Module Basics

// Named exports
export function formatDate(date) { ... }
export const API_URL = 'https://api.example.com';
 
// Default export (one per module)
export default class UserService { ... }
 
// Named imports
import { formatDate, API_URL } from './utils.js';
 
// Default import (any name)
import UserService from './services/user.js';
 
// Namespace import
import * as utils from './utils.js';
utils.formatDate(new Date());
 
// Rename
import { formatDate as format } from './utils.js';

ES Modules vs CommonJS

FeatureES ModulesCommonJS
Syntaximport / exportrequire() / module.exports
LoadingStatic (parsed at compile time)Dynamic (evaluated at runtime)
BindingLive bindings (read-only reference)Value copy
Tree shakingYes (static analysis possible)No (dynamic, can't analyze)
Top-level awaitYesNo
thisundefinedThe exports object
EnvironmentBrowser + NodeNode (originally)

Live Bindings (Critical Difference)

// counter.js (ES Module)
export let count = 0;
export function increment() { count++; }
 
// app.js
import { count, increment } from './counter.js';
console.log(count);  // 0
increment();
console.log(count);  // 1 — live binding, sees updated value!
 
// CommonJS equivalent would show 0 both times (value copy)

Dynamic Imports

// Load module on demand (returns Promise)
const module = await import('./heavy-library.js');
module.doThing();
 
// Conditional loading
if (feature.enabled) {
  const { analyze } = await import('./analytics.js');
  analyze();
}
 
// Pattern: lazy-load based on user action
button.addEventListener('click', async () => {
  const { openEditor } = await import('./editor.js');
  openEditor();
});

Dynamic imports are the foundation of code splitting in webpack, Vite, and Next.js.

Proxy & Reflect

Proxy creates a trap layer around any object, intercepting fundamental operations:

const handler = {
  get(target, property, receiver) {
    console.log(`Reading ${String(property)}`);
    return Reflect.get(target, property, receiver);
  },
 
  set(target, property, value, receiver) {
    console.log(`Writing ${String(property)} = ${value}`);
    return Reflect.set(target, property, value, receiver);
  },
};
 
const user = new Proxy({ name: 'Alice' }, handler);
user.name;        // Logs: Reading name → "Alice"
user.age = 30;    // Logs: Writing age = 30

Proxy Traps

TrapInterceptsExample Use
getProperty readValidation, logging, default values
setProperty writeValidation, reactivity, tracking
hasin operatorCustom containment checks
deletePropertydelete operatorPrevent deletion
applyFunction callLogging, memoization
constructnew operatorSingleton, validation

Real-World Patterns

// Reactive state (how Vue 3 works internally)
function reactive(target) {
  return new Proxy(target, {
    set(obj, prop, value) {
      const oldValue = obj[prop];
      obj[prop] = value;
      if (oldValue !== value) {
        notify(prop, value); // Trigger UI update
      }
      return true;
    },
  });
}
 
// Validation proxy
function validated(target, schema) {
  return new Proxy(target, {
    set(obj, prop, value) {
      const rule = schema[prop];
      if (rule && !rule(value)) {
        throw new TypeError(`Invalid value for ${String(prop)}: ${value}`);
      }
      obj[prop] = value;
      return true;
    },
  });
}
 
const user = validated({}, {
  age: (v) => typeof v === 'number' && v >= 0 && v <= 150,
  email: (v) => typeof v === 'string' && v.includes('@'),
});
 
user.age = 25;    // OK
user.age = -5;    // TypeError
user.email = 'bad'; // TypeError
 
// Auto-vivification (create nested objects on access)
function deepProxy() {
  return new Proxy({}, {
    get(target, prop) {
      if (!(prop in target)) target[prop] = deepProxy();
      return target[prop];
    },
  });
}
 
const config = deepProxy();
config.database.host.primary = 'localhost'; // No intermediate assignments needed

Generators & Iterators

Iterators — The Protocol

Any object with a [Symbol.iterator]() method that returns { next() → { value, done } } is iterable:

const range = {
  from: 1,
  to: 5,
 
  [Symbol.iterator]() {
    let current = this.from;
    const last = this.to;
    return {
      next() {
        return current <= last
          ? { value: current++, done: false }
          : { done: true };
      },
    };
  },
};
 
for (const n of range) console.log(n); // 1, 2, 3, 4, 5
[...range]; // [1, 2, 3, 4, 5]

Generators — Pausable Functions

function* fibonacci() {
  let [a, b] = [0, 1];
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}
 
const fib = fibonacci();
fib.next(); // { value: 0, done: false }
fib.next(); // { value: 1, done: false }
fib.next(); // { value: 1, done: false }
fib.next(); // { value: 2, done: false }
 
// Take first N values
function* take(iterable, n) {
  let count = 0;
  for (const item of iterable) {
    if (count >= n) return;
    yield item;
    count++;
  }
}
 
[...take(fibonacci(), 8)]; // [0, 1, 1, 2, 3, 5, 8, 13]

Async Generators — Streaming Data

async function* fetchPages(url) {
  let page = 1;
  while (true) {
    const res = await fetch(`${url}?page=${page}`);
    const data = await res.json();
 
    if (data.items.length === 0) return;
    yield* data.items;
    page++;
  }
}
 
for await (const item of fetchPages('/api/products')) {
  renderProduct(item);
}

Symbol — Metaprogramming Keys

// Unique property keys (no collisions)
const ID = Symbol('id');
const user = { [ID]: 42, name: 'Alice' };
 
// Well-known symbols customize language behavior
class Money {
  constructor(amount, currency) {
    this.amount = amount;
    this.currency = currency;
  }
 
  [Symbol.toPrimitive](hint) {
    if (hint === 'number') return this.amount;
    if (hint === 'string') return `${this.amount} ${this.currency}`;
    return this.amount;
  }
 
  [Symbol.iterator]() {
    return [this.amount, this.currency][Symbol.iterator]();
  }
}
 
const price = new Money(42, 'USD');
+price;             // 42
`${price}`;         // "42 USD"
const [amt, cur] = price; // [42, "USD"]

Modern Features (ES2022-2024)

// Top-level await (ES2022)
const config = await fetch('/config.json').then(r => r.json());
 
// Array.at() — negative indexing
const last = [1, 2, 3].at(-1); // 3
 
// Object.hasOwn() — better than hasOwnProperty
Object.hasOwn(obj, 'key'); // true/false
 
// structuredClone — deep copy
const copy = structuredClone(complex);
 
// Array grouping (ES2024)
const grouped = Object.groupBy(users, (u) => u.role);
// { admin: [...], user: [...] }
 
// Promise.withResolvers (ES2024)
const { promise, resolve, reject } = Promise.withResolvers();
 
// Set methods (ES2025)
const a = new Set([1, 2, 3]);
const b = new Set([2, 3, 4]);
a.union(b);         // Set {1, 2, 3, 4}
a.intersection(b);  // Set {2, 3}
a.difference(b);    // Set {1}

Interview Signal

Senior candidates demonstrate:

  1. Module system understanding — Live bindings, static analysis, why tree shaking needs ESM
  2. Proxy mechanics — How reactivity systems work, validation patterns, Reflect
  3. Iterator/generator fluency — Custom iterables, async generators for streaming
  4. Practical application — Dynamic imports for code splitting, Symbol for metaprogramming
  5. Modern JS awareness — Keeping up with the language (groupBy, Set methods, structuredClone)