Skip to main content

JavaScript Performance

JavaScript Proxy

Written by Published

A Proxy wraps an object and lets you intercept what happens when it is used.

Reading a property, writing one, even checking if it exists - all of it can be intercepted.

This is how libraries build reactive objects that notice when you change them.

Example

Example

javascript

const target = { name: "Ada" };

const handler = {
  get(obj, key) {
    return obj[key];
  }
};

const proxy = new Proxy(target, handler);

console.log(proxy.name);

The get trap ran, and simply passed the read straight through.

Target, Handler and Traps

The target is the real object being wrapped.

The handler defines traps, functions that intercept an operation.

get and set are the two traps you will use most.

Syntax

Syntax

javascript

const proxy = new Proxy(target, {
  get(obj, key) { return obj[key]; },
  set(obj, key, value) { obj[key] = value; return true; }
});

A set trap must return true to signal success.

Logging Every Read

The trap runs on every property access, without the object itself knowing.

Example

Example

javascript

const target = { name: "Ada", age: 36 };
const reads = [];

const proxy = new Proxy(target, {
  get(obj, key) {
    reads.push(key);
    return obj[key];
  }
});

proxy.name;
proxy.age;

console.log(reads.join(","));

The output is name,age.

Validating on Write

A set trap can reject a bad value before it is ever stored.

Example

Example

javascript

const target = { age: 0 };

const proxy = new Proxy(target, {
  set(obj, key, value) {
    if (key === "age" && value < 0) {
      return false;
    }
    obj[key] = value;
    return true;
  }
});

proxy.age = -5;
console.log(proxy.age);

proxy.age = 30;
console.log(proxy.age);

The negative value was refused; the valid one was stored.

Default Values for Missing Properties

The trap can supply a fallback instead of returning undefined.

Example

Example

javascript

const target = { theme: "dark" };

const proxy = new Proxy(target, {
  get(obj, key) {
    return key in obj ? obj[key] : "not set";
  }
});

console.log(proxy.theme);
console.log(proxy.missing);

The output is dark then not set.

The Original Object Is Unaffected

Reading through the target directly skips the trap entirely.

Example

Example

javascript

const target = { name: "Ada" };
let reads = 0;

const proxy = new Proxy(target, {
  get(obj, key) {
    reads++;
    return obj[key];
  }
});

target.name;
proxy.name;

console.log(reads);

The output is 1: only the read through the proxy was intercepted.

The has Trap

Intercepts the in operator too.

Example

Example

javascript

const target = { name: "Ada" };

const proxy = new Proxy(target, {
  has(obj, key) {
    return key === "name" ? true : key in obj;
  }
});

console.log("name" in proxy);
console.log("age" in proxy);

The output is true then false.

Complete Example

Complete Example

html

<!DOCTYPE html>
<html>
<head>
  <title>JavaScript Proxy</title>
</head>
<body>

  <h1>Proxy</h1>

  <p id="out"></p>

  <script>
    const user = { name: "Ada", age: 36 };

    const validated = new Proxy(user, {
      set(obj, key, value) {
        if (key === "age" && (typeof value !== "number" || value < 0)) {
          return false;
        }
        obj[key] = value;
        return true;
      }
    });

    validated.age = -10;
    const rejected = validated.age;

    validated.age = 40;
    const accepted = validated.age;

    document.getElementById("out").textContent =
      "After rejecting -10: " + rejected + ", after 40: " + accepted;
  </script>

</body>
</html>

Try It Yourself

Run the above example in the Try It Editor.

Try a get trap:

Add a get trap that uppercases every string it returns.

Important Points

  • A Proxy wraps a target object with a handler of traps.
  • get intercepts reading a property.
  • set intercepts writing one, and must return true to succeed.
  • Reading the original target directly bypasses the proxy entirely.
  • has intercepts the in operator.

Conclusion

Proxy is how JavaScript lets you customise what a plain object does.

Validation and logging are the two most common everyday uses.

Frameworks use exactly this mechanism to notice when your data changes.