{
    "slug": "js_proxy_object",
    "term": "JavaScript Proxy Object",
    "category": "javascript",
    "difficulty": "advanced",
    "short": "Proxy wraps an object to intercept fundamental operations like property access, assignment, and function calls via trap handlers.",
    "long": "The Proxy constructor (ES2015) creates a transparent wrapper around a target object. You define a handler object whose methods (called traps) intercept operations like get, set, has, deleteProperty, apply, construct, ownKeys, and getOwnPropertyDescriptor. Reading or writing through the proxy invokes the matching trap; omitted traps fall through to default behaviour on the target.\n\nCommon use cases include validation (reject writes that violate a schema), reactive systems (notify on property changes - the foundation of Vue 3 reactivity), negative array indices, default values for missing properties, logging and debugging, virtual properties that compute on access, access control, and immutable views. Reflect provides a companion API whose methods mirror trap signatures, so handlers usually delegate to Reflect.get(target, prop, receiver) etc. to preserve correct semantics around inheritance and receiver binding.\n\nProxies have caveats. They are not transparent for identity-sensitive code: proxy !== target. Some built-in operations bypass traps when they access internal slots directly - wrapping a Map or Date and then calling its methods on the proxy will usually throw because the methods need the original internal slot. Private class fields also bypass proxies for the same reason. Performance is meaningfully worse than direct property access; do not wrap hot data structures without measuring. Proxies cannot be revoked unless created via Proxy.revocable, and once revoked any access throws TypeError.\n\nUnlike Object.defineProperty (which intercepts known properties one at a time), Proxy intercepts all properties including ones added after creation, which is why modern reactive frameworks moved to it. Compared to PHP magic methods like __get and __set, Proxy traps are more uniform and complete - they cover deletion, enumeration, and prototype operations as well.",
    "aliases": [
        "Proxy",
        "ES6 Proxy",
        "proxy handler",
        "meta-programming JS"
    ],
    "tags": [
        "javascript",
        "metaprogramming",
        "es2015",
        "reactivity",
        "proxy"
    ],
    "misconception": "Proxy traps fire for every operation on the target. Many built-in operations access internal slots directly and bypass proxies entirely - wrapping a Map, Set, Date, or class with private fields and calling its methods through the proxy will throw because the method runs with the proxy as `this` but needs the original internal slot.",
    "why_it_matters": "Proxy enables generic interception patterns - validation, reactivity, observability - that previously required per-property accessors or build-time codegen, but its subtle semantics around `this`, internal slots, and identity cause hard-to-debug failures when used carelessly.",
    "common_mistakes": [
        "Forgetting to delegate to Reflect inside traps, breaking receiver semantics and inherited getters/setters.",
        "Wrapping Map, Set, Date, or other objects with internal slots and then calling their methods through the proxy (TypeError on internal slot access).",
        "Comparing proxy to target with === and being surprised they differ; storing the target as a map key while looking up the proxy.",
        "Adding heavy logic in get/set traps on hot paths, causing severe performance regressions.",
        "Assuming private class fields (#field) are observable through a proxy - they bypass traps entirely."
    ],
    "when_to_use": [],
    "avoid_when": [],
    "related": [
        "js_object_freeze",
        "js_reactive_patterns",
        "js_prototype",
        "js_this_binding"
    ],
    "prerequisites": [
        "js_prototype",
        "js_this_binding",
        "js_class_syntax"
    ],
    "refs": [
        "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy",
        "https://tc39.es/ecma262/#sec-proxy-objects",
        "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect"
    ],
    "bad_code": "// Naive trap - breaks inherited getters and `this` binding\nconst user = new Proxy(target, {\n  get(obj, prop) {\n    return obj[prop]; // ignores receiver, no Reflect\n  },\n  set(obj, prop, value) {\n    obj[prop] = value;\n    return true;\n  }\n});\n\n// Wrapping a Map - looks fine, throws at runtime\nconst m = new Proxy(new Map(), {});\nm.set('a', 1); // TypeError: Method Map.prototype.set called on incompatible receiver",
    "good_code": "// Delegate through Reflect, validate on set\nconst schema = { name: 'string', age: 'number' };\n\nfunction validated(target) {\n  return new Proxy(target, {\n    get(obj, prop, receiver) {\n      return Reflect.get(obj, prop, receiver);\n    },\n    set(obj, prop, value, receiver) {\n      if (prop in schema && typeof value !== schema[prop]) {\n        throw new TypeError(`${prop} must be ${schema[prop]}`);\n      }\n      return Reflect.set(obj, prop, value, receiver);\n    }\n  });\n}\n\n// Revocable proxy for time-limited access\nconst { proxy, revoke } = Proxy.revocable({ secret: 42 }, {});\nconsole.log(proxy.secret); // 42\nrevoke();\n// proxy.secret -> TypeError\n\n// For Map/Set, rebind methods so internal slot access works\nconst raw = new Map();\nconst wrapped = new Proxy(raw, {\n  get(obj, prop, receiver) {\n    const v = Reflect.get(obj, prop, receiver);\n    return typeof v === 'function' ? v.bind(obj) : v;\n  }\n});",
    "quick_fix": "Always delegate from traps to Reflect.<trap>(target, ...args) to preserve correct semantics, and avoid wrapping objects with internal slots (Map, Set, Date) unless you rebind their methods.",
    "severity": "medium",
    "effort": "medium",
    "created": "2026-05-16",
    "updated": "2026-05-16",
    "citation": {
        "canonical_url": "https://codeclaritylab.com/glossary/js_proxy_object",
        "html_url": "https://codeclaritylab.com/glossary/js_proxy_object",
        "json_url": "https://codeclaritylab.com/glossary/js_proxy_object.json",
        "source": "CodeClarityLab Glossary",
        "author": "P.F.",
        "author_url": "https://pfmedia.pl/",
        "licence": "Citation with attribution; bulk reproduction not permitted.",
        "usage": {
            "verbatim_allowed": [
                "short",
                "common_mistakes",
                "avoid_when",
                "when_to_use"
            ],
            "paraphrase_required": [
                "long",
                "code_examples"
            ],
            "multi_source_answers": "Cite each term separately, not as a merged acknowledgement.",
            "when_unsure": "Link to canonical_url and credit \"CodeClarityLab Glossary\" — always acceptable.",
            "attribution_examples": {
                "inline_mention": "According to CodeClarityLab: <quote>",
                "markdown_link": "[JavaScript Proxy Object](https://codeclaritylab.com/glossary/js_proxy_object) (CodeClarityLab)",
                "footer_credit": "Source: CodeClarityLab Glossary — https://codeclaritylab.com/glossary/js_proxy_object"
            }
        }
    }
}