return Reflect.get(...arguments);
Reflect.get
refers to the getter if any. The foo
getter is given the proxied object, receiver
(receiver === case1
), for its this
. Meaning, the get
trap for the bar
is called as well.
const case1 = new Proxy({
get foo() {
console.log("The foo getter", this);
return this.bar;
},
bar: 3
}, {
get(target, property, receiver) {
console.log("The Proxy get trap", ...arguments);
return Reflect.get(...arguments);
}
});
console.log(case1.foo);
> case1.foo
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The foo getter ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶bar ▶Proxy {bar: 3}
▶3
return target[property];
Using the unproxied object, target
. This also triggers the foo
getter, but notice: this
for the foo
getter is the unproxied object, target
. The get
trap for the bar
is not called.
const case2 = new Proxy({
get foo() {
console.log("The foo getter", this);
return this.bar;
},
bar: 3
}, {
get(target, property, receiver) {
console.log("The Proxy get trap", ...arguments);
return target[property];
}
});
console.log(case2.foo);
> case2.foo
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The foo getter ▶{bar: 3}
▶3
return receiver[property];
Using the proxied object, receiver
(receiver === case3
). receiver[property]
refers to the get
trap, not the getter, causing an infinity loop.
const case3 = new Proxy({
get foo() {
console.log("The foo getter", this);
return this.bar;
},
bar: 3
}, {
get(target, property, receiver) {
console.log("The Proxy get trap", ...arguments);
return receiver[property];
}
});
console.log(case3.foo);
> case3.foo
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
▶The Proxy get trap ▶{bar: 3} ▶foo ▶Proxy {bar: 3}
……
Uncaught RangeError: Maximum call stack size exceeded
Now you get it.
Which to use
Can't understand why I should use Reflect.get(obj, 'foo')
instead of obj['foo']
While using the Reflect
verbs is idiomatic for Proxy
trap implementation, there's actually no should. It depends on your use case. If your target
("unproxied") object does not have getters or you're not insterested in what properties its getters are accessing ("secondary property accesses"), you might not need the fancy-looking Reflect
. On the other hand, if you'd like to trigger the trap for all kinds of property accesses, primary or secondary, you would need Reflect
.
For me, I always stick to return Reflect.get(...arguments);
.