JavaScript: Advanced ES6+ Features

Explore modules, Symbols, generators, Maps, Sets, Proxy, and the modern operators that power today's JavaScript ecosystem.

Modules: export and import

ES modules let you split code across files. Each file is its own scope, you explicitly export what you want to share and import what you need.

ES Modules

Modules replace global scripts each file has its own isolated scope by default.

  • Named export: export const fn = ... imported with the exact name
  • Default export: export default fn one per file, imported with any name
  • Namespace import: import * as math from "./math.js"
  • Modules are always in strict mode and run deferred after HTML parsing

Named and Default Exports

Export individual values by name or a single default, import both in one statement.

Dynamic Imports

import() loads a module on demand at runtime and returns a Promise, enabling code splitting and lazy loading.

Dynamic import()

Dynamic imports let you load heavy features only when the user actually needs them.

  • Returns a Promise resolving to the module namespace object
  • The path can be a variable or expression unlike static imports
  • Supported by all modern bundlers (Vite, Webpack) for automatic code splitting
  • Works in both ES modules and classic scripts

Dynamic import()

Defer loading heavy modules until they are actually needed at runtime.

Symbols

A Symbol is a primitive value that is always unique. No two Symbols are ever equal, making them ideal as property keys that never clash with any other code.

Symbol

Symbols are the only primitive type guaranteed to be unique on every creation.

  • Create with Symbol("description"), the description is for debugging only
  • Symbol keys are hidden from for...in, Object.keys(), and JSON.stringify()
  • Symbol.for("key") creates a global shared symbol, same key returns the same Symbol
  • Built-in well-known Symbols: Symbol.iterator, Symbol.toPrimitive

Symbols as Unique Keys

Symbol keys are unique and invisible to standard enumeration.

Iterators and the Iterable Protocol

An iterable is any object with a [Symbol.iterator] method that returns an iterator. An iterator has a next() method returning { value, done }.

Iterable Protocol

Any object that implements Symbol.iterator works with for...of, spread, and destructuring.

  • Arrays, strings, Maps, Sets, and generators are built-in iterables
  • Custom iterables let you define how your object is traversed
  • Iterator returns { value, done: false } per step and { value: undefined, done: true } at end

Custom Iterable

Implement Symbol.iterator to make any object work with for...of and spread.

Generator Functions

A generator function (function*) can pause with yield and resume later, producing values lazily on demand.

Generator Functions

Generators produce values on demand useful for infinite sequences, lazy evaluation, and async flows.

  • Calling a generator function returns an iterator the body does not run immediately
  • yield pauses the function and returns a value to the caller
  • The next .next() call resumes from where it paused
  • Generators are automatically iterable use them with for...of

Generator Functions

Pause with yield, resume with next(), create lazy sequences including infinite ones.

Map

A Map is a key-value collection that accepts any value as a key, preserves insertion order, and has a cleaner API than a plain object.

Map

Use Map when keys are not strings, when insertion order matters, or when you frequently add and remove keys.

  • map.set(key, value), map.get(key), map.has(key), map.delete(key)
  • map.size gives the count instantly
  • Iterable: loop with for...of over map.entries(), map.keys(), map.values()
  • Keys are compared by identity (===), objects and functions can be keys

Map

Key-value pairs with any key type, guaranteed insertion order, and clean iteration.

Set

A Set is a collection of unique values. Duplicates are silently ignored, making it the simplest way to deduplicate an array.

Set

Use Set when uniqueness matters: deduplication, membership checks, set operations.

  • set.add(value), set.has(value), set.delete(value), set.size
  • Deduplicate an array: [...new Set(array)]
  • Iterable: use for...of directly in insertion order
  • Values compared by identity, two different objects are always unique entries

Set

Unique collections, easy deduplication, and clean set operations.

WeakMap and WeakSet

Weak variants hold references to objects without preventing garbage collection. When an object is no longer referenced elsewhere, its entry is automatically removed.

WeakMap and WeakSet

Use weak collections to attach metadata to objects without causing memory leaks.

  • WeakMap: keys must be objects entries are removed when the key is GC'd
  • WeakSet: values must be objects removed when the object is GC'd
  • Neither is iterable no size, no forEach, no keys()
  • Common use: caching DOM node metadata, storing private state per instance

WeakMap for Private Metadata

Attach data to objects without preventing them from being garbage-collected.

Proxy and Reflect

Proxy wraps an object and intercepts operations on it. Reflect provides default implementations for those same operations.

Proxy and Reflect

Proxy powers Vue 3's reactivity system, validation libraries, and observable patterns.

  • new Proxy(target, handler) handler defines traps: get, set, deleteProperty
  • Call Reflect.set(target, key, value) inside a trap to perform the default behaviour
  • Common uses: input validation on assignment, change tracking, logging
  • The proxy is transparent to the outside callers do not know they are using a proxy

Proxy with Validation

Intercept property writes to enforce rules before storing the value.

Optional Chaining (?.)

The optional chaining operator short-circuits to undefined instead of throwing when a property in the chain is null or undefined.

Optional Chaining (?.)

Replace deep null-checks with ?. for safe property access on possibly-missing values.

  • Property access: user?.address?.city
  • Method call: user?.getProfile?.()
  • Array index: arr?.[0]
  • Combine with ?? to provide a fallback: user?.age ?? "unknown"

Optional Chaining

Access deeply nested properties safely, undefined instead of a TypeError.

Nullish Coalescing (??)

The nullish coalescing operator returns the right-hand side only when the left-hand side is null or undefinedunlike || which also triggers on0, "", and false.

Nullish Coalescing (??)

Use ?? for default values when 0, false, or empty string are valid and should not be replaced.

  • a ?? b: returns b only if a is null or undefined
  • a || b: returns b for any falsy a including 0, "", false
  • Nullish assignment: user.role ??= "guest" assigns only if currently null/undefined
  • Combine with ?.: user?.score ?? 0

?? vs ||

?? preserves intentional falsy values like 0 and '' || replaces all of them.

Knowledge Check

1. What is the difference between a named export and a default export?

2. What makes a Symbol unique?

3. What does a generator function return when called?

4. What is the key difference between Map and a plain object?

5. What does optional chaining (?.) return when a property in the chain is null or undefined?

6. When does the nullish coalescing operator (??) use the right-hand side value?

7. What is a WeakMap different from a regular Map?