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.
Preface
Vue 2’s reactivity system uses
Object.definePropertyfor 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
Proxyfor reactivity, packaged in the
@vue/reactivitymodule. 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.getinside a trap preserves the correct
thisbinding:
<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
gettrap 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)
WeDoctor Frontend Technology
Official WeDoctor Group frontend public account, sharing original tech articles, events, job postings, and occasional daily updates from our tech team.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.