Skip to content

17 — Metaprogramming

One line: JavaScript lets a program change how the language treats its objects — with well-known symbols, proxies, template tags, decorators and using — and each hook comes with rules the engine enforces even when your code does not.

The chapter moves from hooks the language calls on your objects to strings run as code. Well-known symbols come first, then Proxy and Reflect, which intercept every operation on an object. Tagged templates hand a function the pieces of a template. Decorators and using are the newest: the first is still a proposal that Node cannot run, the second shipped in Node 24. The chapter ends with eval, new Function, and the flag that switches both off.

The pages

Page Level In one line Status
Well-known symbols 301 Node lists 15 well-known symbols, each a hook the language calls on your objects: a static [Symbol.hasInstance] makes 2 instanceof Even answer true, and [Symbol.toStringTag] makes Object.prototype.toString report [object Array] for an object that is no array. stub
Proxy and Reflect 301 A Proxy sees only the operations you trap — "a" in p never reaches a get trap, because in is the has trap — and it cannot lie about a frozen target: returning 0 for a frozen answer: 42 throws a TypeError. stub
Tagged templates 201 A tag receives the literal pieces and the values separately — always one more piece than values — and the pieces array is frozen and is the same object on every call from one place in the source, with .raw keeping backslashes as typed. stub
Decorators 301 Decorators are still a TC39 proposal, so Node 25.2.1 cannot run them — a .ts file with @logged fails with SyntaxError once its types are stripped — while TypeScript 7.0.2 type-checks the standard form and compiles it to JavaScript that Node runs. stub
using and Symbol.dispose 301 using calls [Symbol.dispose]() as its block exits — in reverse order of declaration, even when the block throws — and when a dispose throws too, both errors survive in one SuppressedError; Node 24 and 25.2.1 run it without a flag. stub
eval and new Function 301 Direct eval sees the caller's local variables, while (0, eval)(code) and new Function(code) see only globals — and starting Node with --disallow-code-generation-from-strings turns all three into an EvalError. stub

Boundaries

Symbols themselves, Symbol.toPrimitive and Symbol.iterator have their own pages in the values, coercion and iteration chapters; this chapter surveys the hooks and covers the rest.