← Back to Tutorials Chapter 7

Advanced Concepts

This chapter explores advanced JavaScript features including asynchronous programming patterns, modules, metaprogramming with Proxy and Reflect, and typed arrays for binary data.

Asynchronous Programming

JavaScript is single-threaded but non-blocking. Asynchronous operations (like network requests or timers) run in the background and execute callbacks when complete. The event loop manages this concurrency model.

console.log("Start");

setTimeout(() => {
  console.log("Timeout callback");
}, 1000);

console.log("End");
// Output: Start, End, Timeout callback

We cover asynchronous programming in depth in Chapter 8: Asynchronous JavaScript.

Modules (export / import)

ES6 modules let you split code into separate files. Use export to expose values and import to consume them:

Named Exports

// 📁 math.js
export const PI = 3.14159;
export function add(a, b) {
  return a + b;
}
export class Calculator { /* ... */ }
// 📁 app.js
import { PI, add } from "./math.js";
console.log(add(PI, 2)); // 5.14159

Default Exports

// 📁 utils.js
export default function formatDate(date) {
  return date.toISOString();
}

// 📁 app.js
import formatDate from "./utils.js";

Dynamic Imports

Load modules on demand with import():

// Dynamic import returns a promise
button.addEventListener("click", async () => {
  const module = await import("./heavy-module.js");
  module.run();
});
Important: Modules are always in strict mode and are deferred by default. Use <script type="module"> in HTML to load module scripts. Module code runs after the DOM is ready.

Metaprogramming with Proxy

A Proxy wraps an object and lets you intercept fundamental operations (getting, setting, deleting properties, etc.) via handler traps:

const target = { message: "Hello" };

const handler = {
  get(obj, prop) {
    if (prop === "message") {
      return obj[prop] + " (intercepted)";
    }
    return obj[prop];
  },
  set(obj, prop, value) {
    console.log(`Setting ${prop} to ${value}`);
    obj[prop] = value;
    return true; // Indicate success
  }
};

const proxy = new Proxy(target, handler);
console.log(proxy.message); // "Hello (intercepted)"
proxy.newProp = 42;         // Logs: "Setting newProp to 42"

Metaprogramming with Reflect

Reflect provides methods that correspond to each Proxy trap. Use Reflect inside proxy handlers for default behavior:

const handler = {
  get(obj, prop, receiver) {
    console.log(`Getting ${prop}`);
    return Reflect.get(obj, prop, receiver);
  },
  set(obj, prop, value, receiver) {
    console.log(`Setting ${prop} = ${value}`);
    return Reflect.set(obj, prop, value, receiver);
  }
};

const proxy = new Proxy({}, handler);
proxy.name = "Alice"; // Logs: Setting name = Alice
console.log(proxy.name); // Logs: Getting name → "Alice"

Typed Arrays

Typed arrays provide views over raw binary data buffers. Each typed array interprets the buffer as a sequence of a specific numeric type:

// Create a typed array with 4 entries (16 bytes total for Int32Array)
const int32 = new Int32Array(4);
int32[0] = 42;
int32[1] = 100;
int32[2] = 255;
int32[3] = 1024;
console.log(int32); // Int32Array [42, 100, 255, 1024]

// Float64Array (8 bytes per element)
const float64 = new Float64Array(3);
float64[0] = 3.14;
float64[1] = 2.718;
float64[2] = 1.618;
console.log(float64); // Float64Array [3.14, 2.718, 1.618]

ArrayBuffer and DataView

ArrayBuffer is a fixed-length raw binary buffer. DataView provides a flexible interface to read/write different types at any byte offset:

const buffer = new ArrayBuffer(16); // 16 bytes

const view = new DataView(buffer);
view.setInt32(0, 12345);   // Write 32-bit integer at byte 0
view.setFloat64(4, 3.1415); // Write 64-bit float at byte 4
view.setUint8(12, 255);    // Write unsigned byte at byte 12

console.log(view.getInt32(0));   // 12345
console.log(view.getFloat64(4)); // 3.1415
console.log(view.getUint8(12));  // 255

// Byte order (endianness)
console.log(view.getInt32(0, true)); // Little-endian read
Advanced JavaScript concepts overview
Use Case: Typed arrays and ArrayBuffers are essential for WebGL graphics, audio processing (Web Audio API), file manipulation (FileReader), and network protocols (WebSockets). They provide fine-grained control over binary data with predictable performance.
Exercise: Create a Proxy that validates property values on an object. The proxy should ensure that any numeric property is between 0 and 100, and any string property is at most 20 characters long. Throw a TypeError with a descriptive message if validation fails. Create a sample object and test both valid and invalid assignments.