Suggest an editImprove this articleRefine the answer for “Proxy traps”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Traps are the methods of a `Proxy` handler that intercept standard operations on an object (reading, writing and deleting a property, the `in` operator, key enumeration, a function call, `new`) and let you redefine their behaviour.** Each trap corresponds to one internal engine operation and has the same signature as the `Reflect` method of the same name, which is why the default behaviour is invoked through `Reflect` from inside the trap. If there is no trap for an operation, it runs on the `target` as usual. ```javascript const proxy = new Proxy({ name: 'Maria' }, { get(target, prop, receiver) { console.log(`Reading ${String(prop)}`); return Reflect.get(target, prop, receiver); // the default behaviour } }); proxy.name; // Reading name, then "Maria" ``` **Key point:** a trap is an interceptor function in the `handler`, and `Reflect` inside it restores the operation's normal semantics.Shown above the full answer for quick recall.Answer (EN)Image**Traps are methods on a `Proxy` handler that intercept standard operations on an object (reading, writing and deleting properties, calling a function, the `in` operator and so on) and let you redefine their behaviour.** Put simply, traps let you step into the work of JavaScript itself and change how an object behaves. ## Theory ### TL;DR - Traps are functions defined in the second argument of `Proxy`, the so-called handler. - Each trap corresponds to one internal operation: `get`, `set`, `has`, `deleteProperty`, `ownKeys`, `apply`, `construct` and others. - A trap signature matches the `Reflect` method of the same name one to one. - Inside a trap the default behaviour is invoked through `Reflect`, not through direct access to the `target`. - If there is no trap for an operation, the operation runs on the `target` unchanged. - The `apply` and `construct` traps only work when the `target` is a function. ### Quick example ```javascript const user = { name: 'Maria' }; const proxy = new Proxy(user, { get(target, property) { console.log(`Reading property "${String(property)}"`); return target[property]; } }); console.log(proxy.name); // Reading property "name" // Maria ``` Here the `get` trap intercepts the property access, and before returning the value you can do anything: log it, check it, validate it or substitute a different value. ### Syntax and the handler ```javascript const proxy = new Proxy(target, handler); ``` | Parameter | Description | | --- | --- | | `target` | The original object, the real "target" | | `handler` | An object of traps, that is, methods that intercept actions | ### The full list of the main traps | Trap | What it intercepts | Counterpart | | --- | --- | --- | | `get(target, prop, receiver)` | Reading a property (`obj.prop`) | `obj[prop]` | | `set(target, prop, value, receiver)` | Writing a property (`obj.prop = value`) | `obj[prop] = value` | | `has(target, prop)` | The `prop in obj` check | `in` | | `deleteProperty(target, prop)` | Deletion (`delete obj[prop]`) | `delete` | | `ownKeys(target)` | Getting the key list (`Object.keys`, `for...in`) | `Object.keys(obj)` | | `getOwnPropertyDescriptor(target, prop)` | A property descriptor request | `Object.getOwnPropertyDescriptor()` | | `defineProperty(target, prop, descriptor)` | Defining a new property | `Object.defineProperty()` | | `getPrototypeOf(target)` | Getting the prototype | `Object.getPrototypeOf()` | | `setPrototypeOf(target, proto)` | Changing the prototype | `Object.setPrototypeOf()` | | `isExtensible(target)` | The extensibility check | `Object.isExtensible()` | | `preventExtensions(target)` | Forbidding new properties | `Object.preventExtensions()` | | `apply(target, thisArg, args)` | A function call | `func()` | | `construct(target, args, newTarget)` | A call with `new` | `new func()` | ### Popular traps for objects **`set`, controlling writes:** ```javascript const user = { name: 'Maria' }; const proxy = new Proxy(user, { set(target, prop, value) { if (prop === 'age' && typeof value !== 'number') { throw new TypeError('Age must be a number'); } target[prop] = value; return true; // returning true is mandatory } }); proxy.age = 25; // ok proxy.age = 'twenty'; // TypeError ``` **`has`, replacing the `in` operator:** ```javascript const secret = { password: '1234' }; const proxy = new Proxy(secret, { has(target, key) { if (key === 'password') return false; return key in target; } }); console.log('password' in proxy); // false ``` This is how you can hide sensitive fields from existence checks. **`ownKeys`, hiding properties from enumeration:** ```javascript const user = { name: 'Maria', password: '12345' }; const proxy = new Proxy(user, { ownKeys(target) { return Object.keys(target).filter(k => k !== 'password'); } }); console.log(Object.keys(proxy)); // ["name"] ``` ### Traps for functions: apply and construct **`apply`, intercepting a function call:** ```javascript function sum(a, b) { return a + b; } const proxy = new Proxy(sum, { apply(target, thisArg, args) { console.log(`Called with arguments: ${args}`); return Reflect.apply(target, thisArg, args); } }); console.log(proxy(2, 3)); // Called with arguments: 2,3 // 5 ``` **`construct`, intercepting a call with `new`:** ```javascript function User(name) { this.name = name; } const proxy = new Proxy(User, { construct(target, args) { console.log('Creating a new user...'); return new target(...args); } }); const maria = new proxy('Maria'); // "Creating a new user..." ``` Both traps only fire when the `target` is a function: you cannot wrap a plain object and intercept `apply` on it. ### Delegating the default behaviour through Reflect To avoid breaking native behaviour, traps usually call `Reflect` inside: it performs the "real" operation under the hood. ```javascript const proxy = new Proxy(user, { get(target, prop, receiver) { console.log(`Reading ${String(prop)}`); return Reflect.get(target, prop, receiver); } }); ``` This guarantees that everything you did not intend to change stays at its default. The third `receiver` argument is not decorative: it passes the correct `this` to getters, whereas a plain `target[prop]` loses that context. ### Where traps are actually used - Validation and data protection. - Logging property access. - Creating "virtual" properties that do not physically exist on the object. - Reactive systems (Vue, MobX). - ORMs and API wrappers, where models are substituted dynamically. - Metaprogramming and test mocks. | Term | What it is | | --- | --- | | **Trap** | A handler method on a `Proxy` that intercepts an operation on the object | | **Handler** | The object that holds the traps | | **Reflect** | Used inside traps to invoke the default behaviour | | **Target** | The real object the `Proxy` stands in front of | ### Common mistakes - **Not returning a boolean from `set`, `deleteProperty`, `has` or `defineProperty`.** These traps must return `true` or `false`; `undefined` counts as `false`, and in strict mode the operation fails with a `TypeError`. - **Using `target[prop]` instead of `Reflect.get(target, prop, receiver)`.** That loses the `receiver`, so getters and inherited properties end up with the wrong `this`. - **Forgetting that `prop` can be a `Symbol`.** Interpolating a symbol key into a template string without `String(prop)` throws a `TypeError`. - **Violating the invariants.** A trap is not omnipotent: `ownKeys` must return every non-configurable own key, and `get` cannot report a different value for a non-configurable, non-writable property. Otherwise the engine throws a `TypeError`. - **Expecting `apply` to work for an object.** The `apply` and `construct` traps only work with functions. - **Assuming traps intercept everything.** Internal slots (for example in `Map`, `Set` or `Date`) reach the real object past the traps, which is why such built-ins break behind a `Proxy` unless their methods are bound.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.