Frontend Development 15 min read

Mastering JavaScript Proxy and Reflect: Deep Dive into Vue 3 Reactivity

This article explains how Vue 3 replaces Vue 2’s Object.defineProperty reactivity with JavaScript’s Proxy, covering Proxy fundamentals, handler traps, the relationship with Reflect, internal methods and slots, and the distinction between ordinary and exotic objects, providing code examples and visual diagrams.

WeDoctor Frontend Technology
WeDoctor Frontend Technology
WeDoctor Frontend Technology
Mastering JavaScript Proxy and Reflect: Deep Dive into Vue 3 Reactivity

Preface

Vue 2’s reactivity system uses

Object.defineProperty

for data hijacking, which has several drawbacks: it must traverse each property of ordinary objects, cannot detect array changes, cannot monitor Map/Set mutations, and cannot observe addition or deletion of object properties.

Vue 3 adopts

Proxy

for reactivity, packaged in the

@vue/reactivity

module. Understanding Proxy is essential for mastering Vue 3’s reactivity.

Proxy Overview

Proxy creates a proxy for an object, allowing interception and customization of fundamental operations such as property lookup, assignment, enumeration, and function invocation.

Basic syntax:

<code>const p = new Proxy(target, handler);</code>

target is the object to be proxied; it can be any object, including arrays, functions, or another Proxy. Primitive values cannot be used as the target.

handler is an object whose properties are functions (traps) that define custom behavior for operations on the proxy.

Common traps include

get

,

set

, and

apply

. Example:

<code>const obj = { foo: 'bar', fn() { console.log('fn called'); } };
const handler = {
  get(target, key) {
    console.log(`property ${key} was read`);
    return target[key];
  },
  set(target, key, value) {
    console.log(`property ${key} was set to ${value}`);
    target[key] = value;
  },
  apply(target, thisArg, args) {
    console.log('function call intercepted');
    return target.apply(thisArg, args);
  }
};
const p = new Proxy(obj, handler);

p.foo;          // logs: property foo was read
p.foo = 'bar1'; // logs: property foo was set to bar1
p.fn();          // logs: property fn was read, then fn called
</code>

Only basic operations are intercepted; compound operations like

obj.fn()

involve a property read followed by a function call, so only the read is trapped.

Reflect and Proxy

Reflect is a built‑in object that provides methods corresponding to the proxy traps.

For example,

Reflect.get(obj, 'foo')

performs the default property access.

<code>const obj = { foo: 'foo' };
obj.foo;               // same as
Reflect.get(obj, 'foo');
</code>

Using

Reflect.get

inside a trap preserves the correct

this

binding:

<code>const handler = {
  get(target, key, receiver) {
    console.log(`property ${key} was read`);
    return Reflect.get(target, key, receiver);
  },
  set(target, key, value, receiver) {
    console.log(`property ${key} was set to ${value}`);
    return Reflect.set(target, key, value, receiver);
  }
};
const obj = { foo: 'foo', get bar() { return this.foo; } };
const p = new Proxy(obj, handler);

p.bar; // triggers both bar and foo reads when bar’s getter accesses this.foo
</code>

How Proxy Works Internally

Internal Methods and Slots

ECMAScript specifies object behavior via internal methods and internal slots, identified by double brackets

[[...]]

. When a property is accessed, the engine invokes the object's

[[Get]]

internal method.

Objects implement a set of fundamental internal methods; functions also implement

[[Call]]

and optionally

[[Construct]]

.

Ordinary vs. Exotic Objects

Ordinary objects conform to the standard internal method definitions (e.g., the ten methods in §10.1.x). Exotic objects deviate from these definitions; Proxy is an exotic object because its internal methods are defined in §10.5.x.

Further Proxy Details

When a proxy’s

get

trap is absent, the operation falls back to the target’s

[[Get]]

. If the trap exists, it handles the operation on the proxy itself, illustrating the proxy’s transparent nature.

<code>const obj = { foo: 'foo' };
const p = new Proxy(obj, {});

p.foo; // outputs: foo
</code>

Conclusion

The article introduced Proxy and its combination with Reflect, explained internal methods and slots from the ECMAScript specification, distinguished ordinary and exotic objects, and clarified how Proxy achieves object interception.

References

Proxy - JavaScript | MDN (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)

Reflect - JavaScript | MDN (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect)

ECMAScript® 2023 Language Specification (https://tc39.es/ecma262/)

Vue.js Design and Implementation (https://www.ituring.com.cn/book/2953)

JavaScriptProxyECMAScriptVue3reactivityReflect
WeDoctor Frontend Technology
Written by

WeDoctor Frontend Technology

Official WeDoctor Group frontend public account, sharing original tech articles, events, job postings, and occasional daily updates from our tech team.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.