You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
JavaScript enforces some invariants -- conditions that must be fulfilled by internal methods and traps.
@@ -76,7 +76,7 @@ Most of them are for return values:
76
76
- ...and so on, we'll see more in examples below.
77
77
78
78
There are some other invariants, like:
79
-
- `[[GetPrototypeOf]]`, applied to the proxy object must return the same value as `[[GetPrototypeOf]]` applied to the proxy object's target object. In other words, reading prototype of a `proxy` must always return the prototype of the target object.
79
+
- `[[GetPrototypeOf]]`, applied to the proxy object must return the same value as `[[GetPrototypeOf]]` applied to the proxy object's target object. In other words, reading prototype of a proxy must always return the prototype of the target object.
80
80
81
81
Traps can intercept these operations, but they must follow these rules.
82
82
@@ -181,8 +181,6 @@ The proxy should totally replace the target object everywhere. No one should eve
181
181
182
182
## Validation with "set" trap
183
183
184
-
Now let's intercept writing as well.
185
-
186
184
Let's say we want an array exclusively for numbers. If a value of another type is added, there should be an error.
187
185
188
186
The `set` trap triggers when a property is written.
@@ -194,7 +192,7 @@ The `set` trap triggers when a property is written.
194
192
-`value` -- property value,
195
193
-`receiver` -- similar to `get` trap, matters only for setter properties.
196
194
197
-
The `set` trap should return `true` if setting is successful, and `false` otherwise (leads to`TypeError`).
195
+
The `set` trap should return `true` if setting is successful, and `false` otherwise (triggers`TypeError`).
As we've seen already, that mostly works. The wrapper function `(*)` performs the call after the timeout.
537
535
538
-
But a wrapper function does not forward property read/write operations or anything else. So if we have a property on the original function, we can't access it after wrapping:
536
+
But a wrapper function does not forward property read/write operations or anything else. After the wrapping, the access is lost to properties of the original functions, such as `name`, `length` and others:
539
537
540
538
```js run
541
539
functiondelay(f, ms) {
@@ -609,9 +607,21 @@ Here are examples of operations and `Reflect` calls that do the same:
In particular, `Reflect` allows to call operators (`new`, `delete`...) as functions. That's an interesting capability, but here another thing is important.
610
+
For example:
611
+
612
+
```js run
613
+
let user = {};
614
+
615
+
Reflect.set(user, 'name', 'John');
616
+
617
+
alert(user.name); // John
618
+
```
619
+
620
+
In particular, `Reflect` allows to call operators (`new`, `delete`...) as functions (`Reflect.construct`, `Reflect.deleteProperty`, ...). That's an interesting capability, but here another thing is important.
621
+
622
+
**For every internal method, trappable by `Proxy`, there's a corresponding method in `Reflect`, with the same name and arguments as `Proxy` trap.**
613
623
614
-
For every internal method, trappable by `Proxy`, there's a corresponding method in `Reflect`, with the same name and arguments as `Proxy` trap. So we can use `Reflect` to forward an operation to the original object.
624
+
So we can use `Reflect` to forward an operation to the original object.
615
625
616
626
In this example both traps `get` and `set` transparently (as if they didn't exist) forward reading/writing operations to the object, showing a message:
617
627
@@ -691,7 +701,7 @@ let user = {
691
701
692
702
let userProxy =newProxy(user, {
693
703
get(target, prop, receiver) {
694
-
return target[prop]; // (*)
704
+
return target[prop]; // (*) target = user
695
705
}
696
706
});
697
707
@@ -710,15 +720,15 @@ Reading `admin.name` should return `"Admin"`, not `"Guest"`!
710
720
711
721
What's the matter? Maybe we did something wrong with the inheritance?
712
722
713
-
But if we remove the proxy (delete lines 8-12), then everything will be fine.
723
+
But if we remove the proxy, then everything will work as expected.
714
724
715
725
The problem is actually in the proxy, in the line `(*)`.
716
726
717
727
1. When we read `admin.name`, as `admin` object doesn't have such own property, the search goes to its prototype.
718
728
2. The prototype is `userProxy`.
719
729
3. When reading `name` property from the proxy, its `get` trap triggers and returns it from the original object as `target[prop]` in the line `(*)`.
720
730
721
-
A call to `target[prop]`, when `prop` is a getter, runs it in the context `this=target`. So the result is `this._name` from the original object `target`, that is: from `user`.
731
+
A call to `target[prop]`, when `prop` is a getter, runs its code in the context `this=target`. So the result is `this._name` from the original object `target`, that is: from `user`.
722
732
723
733
To fix such situations, we need `receiver`, the third argument of `get` trap. It keeps the correct `this` to be passed to a getter. In our case that's `admin`.
0 commit comments