Suggest an editImprove this articleRefine the answer for “The Proxy object”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**`Proxy` is a wrapper around an object that intercepts operations on it: reading and writing properties, the `in` check, deletion, key enumeration, function calls and `new`.** It is created as `new Proxy(target, handler)`, where `target` is the original object and `handler` is an object with traps: `get`, `set`, `has`, `deleteProperty`, `ownKeys`, `apply`, `construct` and others. Inside a trap you can substitute the result, throw an error, log the access or return a default value, and to run the standard behaviour you call the matching `Reflect` method. `Proxy` is exactly what Vue 3 reactivity is built on, as well as data validation, property protection and dynamic API wrappers. ```javascript const user = new Proxy({}, { set(target, prop, value) { if (prop === 'age' && typeof value !== 'number') { throw new TypeError('Age must be a number'); } target[prop] = value; return true; // without true you get a TypeError in strict mode } }); user.age = 30; // ok ``` **Key point:** `Proxy` stands between your code and the object and lets you redefine any basic operation, while `Reflect` gives you the default behaviour back.Shown above the full answer for quick recall.Answer (EN)Image**`Proxy` is a wrapper around an object that lets you intercept operations on it.** Put simply, `Proxy` gives you a way to stand between your code and the object so you can control what happens when properties are read, changed or otherwise touched. ## Theory ### TL;DR - `new Proxy(target, handler)` creates a wrapper around the original object. - `handler` is an object with traps, each of which intercepts one operation. - The main traps are `get`, `set`, `has`, `deleteProperty`, `ownKeys`, `apply` and `construct`. - The `set` and `deleteProperty` traps must return `true`, otherwise strict mode throws a `TypeError`. - `Reflect` holds methods matching every trap and performs the default behaviour. - Typical uses: validation, logging, default values, property protection, Vue 3 reactivity, i18n with a fallback. ### Quick example ```javascript const user = { name: 'Tim', age: 25 }; const proxy = new Proxy(user, { get(target, prop) { console.log(`Reading property "${prop}"`); return target[prop]; }, set(target, prop, value) { console.log(`Changing "${prop}" to "${value}"`); target[prop] = value; return true; // you must return true, otherwise there will be an error } }); console.log(proxy.name); // Reading property "name" -> "Tim" proxy.age = 26; // Changing "age" to "26" ``` ### Syntax ```javascript const proxy = new Proxy(target, handler); ``` | Parameter | Description | | --- | --- | | `target` | the original object the proxy is built over | | `handler` | an object with traps that intercept operations | A proxy does not copy the object: every operation eventually reaches `target`, as long as the trap passes it through. If there is no trap for an operation, it runs exactly as if the `Proxy` did not exist. ### Practical trap examples **Validation on write.** The `set` trap checks the type and refuses to store garbage in the object. ```javascript const user = new Proxy({}, { set(target, prop, value) { if (prop === 'age' && typeof value !== 'number') { throw new TypeError('Age must be a number'); } target[prop] = value; return true; } }); user.age = 30; // ok user.age = 'thirty'; // TypeError ``` **Default values.** The `get` trap returns a stand-in value for properties that do not exist. ```javascript const config = new Proxy({}, { get(target, prop) { return prop in target ? target[prop] : '<not set>'; } }); console.log(config.host); // <not set> config.host = 'localhost'; console.log(config.host); // localhost ``` **Protection from deletion.** The `deleteProperty` trap forbids removing a key field. ```javascript const secure = new Proxy({ id: 1, name: 'Admin' }, { deleteProperty(target, prop) { if (prop === 'id') { console.log('Deletion is forbidden!'); return false; } delete target[prop]; return true; } }); delete secure.name; // gets deleted delete secure.id; // Deletion is forbidden! ``` ### Reflect and the default behaviour `Reflect` is a set of methods matching all the `Proxy` traps. It lets you invoke the **default behaviour** whenever you need it. ```javascript function createLoggedObject(obj) { return new Proxy(obj, { get(target, prop, receiver) { console.log(`Access to property "${prop}"`); return Reflect.get(target, prop, receiver); // the standard behaviour } }); } const user = createLoggedObject({ name: 'Tim' }); console.log(user.name); // Access to property "name" ``` Without `Reflect` you would have to write `target[prop]` by hand, and that can break inheritance and `this`: a getter on the prototype receives the wrong `receiver` and reads fields from `target` instead of from the proxy. ### Available traps | Trap | What it intercepts | Reflect equivalent | | --- | --- | --- | | `get` | Reading a property | `Reflect.get()` | | `set` | Writing a property | `Reflect.set()` | | `has` | The `prop in obj` check | `Reflect.has()` | | `deleteProperty` | Deletion, `delete obj[prop]` | `Reflect.deleteProperty()` | | `ownKeys` | Getting all keys (`Object.keys`, `for...in`) | `Reflect.ownKeys()` | | `getOwnPropertyDescriptor` | A property descriptor | `Reflect.getOwnPropertyDescriptor()` | | `defineProperty` | Creating or changing a property | `Reflect.defineProperty()` | | `preventExtensions` | Forbidding new properties | `Reflect.preventExtensions()` | | `isExtensible` | Checking whether the object is extensible | `Reflect.isExtensible()` | | `getPrototypeOf` | Getting the prototype | `Reflect.getPrototypeOf()` | | `setPrototypeOf` | Changing the prototype | `Reflect.setPrototypeOf()` | | `apply` | Calling a function | `Reflect.apply()` | | `construct` | Calling through `new` | `Reflect.construct()` | You can proxy not only a plain object but a function too, and then the `apply` and `construct` traps come into play: ```javascript function sum(a, b) { return a + b; } const proxySum = new Proxy(sum, { apply(target, thisArg, args) { console.log(`Calling the function with arguments: ${args}`); return Reflect.apply(target, thisArg, args); } }); console.log(proxySum(2, 3)); // Calling the function with arguments: 2,3 -> 5 ``` ### Where Proxy is genuinely useful | Scenario | Example of use | | --- | --- | | **Data validation** | Type checking on write | | **Debugging and logging** | Intercepting `get` and `set` for analysis | | **Reactivity (Vue 3)** | Tracking property changes | | **Protection and restrictions** | Forbidding deletion or modification of properties | | **Localisation and key access** | i18n, fallback data | | **API wrappers** | "Smart" dynamic objects, a REST API built on the fly for instance | An example of a dynamic structure for i18n: instead of an error or `undefined` the user sees the key itself, and translators immediately notice what is missing. ```javascript const dictionary = { hello: 'Hello', world: 'world' }; const i18n = new Proxy(dictionary, { get(target, key) { if (key in target) return target[key]; return `[${key}]`; // substitute the key instead of a translation } }); console.log(i18n.hello); // Hello console.log(i18n.missingKey); // [missingKey] ``` A short summary of the terms: | Term | Description | | --- | --- | | `Proxy` | An object that intercepts operations on another object | | `target` | The original object (the target) | | `handler` | An object with traps (intercepting methods) | | `trap` | A method intercepting a particular operation (`get`, `set` and so on) | | `Reflect` | Lets you invoke the standard default behaviour | ### Common mistakes - **Forgetting `return true` in `set` or `deleteProperty`.** The trap has to tell the engine that the operation succeeded. In strict mode, and modules are always strict, returning `false` or `undefined` leads to a `TypeError`. - **Writing `target[prop]` instead of `Reflect.get(target, prop, receiver)`.** The `receiver` is lost, so getters from the prototype get the wrong `this`. - **Assuming the proxy changes the object itself.** Traps fire only for accesses that go through the proxy. If a reference to `target` is still around somewhere, every check is bypassed. - **Breaking the object invariants.** For a non-configurable property the `get` trap cannot return a different value, and for a non-extensible object `ownKeys` cannot invent new keys: the engine throws a `TypeError`. - **Wrapping everything in a proxy.** Every operation goes through your code, so a proxy is noticeably slower than a plain object in a hot loop. - **Expecting `Proxy` to be polyfillable.** Interception is implemented at the engine level, so there is no substitute for old environments without support.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.