Skip to content

Commit a2daa01

Browse files
committed
minor
1 parent 763aba5 commit a2daa01

File tree

1 file changed

+22
-12
lines changed

1 file changed

+22
-12
lines changed

1-js/99-js-misc/01-proxy/article.md

+22-12
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ For every internal method, there's a trap in this table: the name of the method
6464
| `[[IsExtensible]]` | `isExtensible` | [Object.isExtensible](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible) |
6565
| `[[PreventExtensions]]` | `preventExtensions` | [Object.preventExtensions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions) |
6666
| `[[DefineOwnProperty]]` | `defineProperty` | [Object.defineProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty), [Object.defineProperties](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties) |
67-
| `[[GetOwnProperty]]` | `getOwnPropertyDescriptor` | [Object.getOwnPropertyDescriptor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor), `for..in` |
68-
| `[[OwnPropertyKeys]]` | `ownKeys` | [Object.keys](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys), [Object.getOwnPropertyNames](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames), [Object.getOwnPropertySymbols](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertySymbols), `for..in` |
67+
| `[[GetOwnProperty]]` | `getOwnPropertyDescriptor` | [Object.getOwnPropertyDescriptor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor), `for..in`, `Object.keys/values/entries` |
68+
| `[[OwnPropertyKeys]]` | `ownKeys` | [Object.getOwnPropertyNames](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames), [Object.getOwnPropertySymbols](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertySymbols), `for..in`, `Object/keys/values/entries` |
6969

7070
```warn header="Invariants"
7171
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:
7676
- ...and so on, we'll see more in examples below.
7777
7878
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.
8080
8181
Traps can intercept these operations, but they must follow these rules.
8282
@@ -181,8 +181,6 @@ The proxy should totally replace the target object everywhere. No one should eve
181181

182182
## Validation with "set" trap
183183

184-
Now let's intercept writing as well.
185-
186184
Let's say we want an array exclusively for numbers. If a value of another type is added, there should be an error.
187185

188186
The `set` trap triggers when a property is written.
@@ -194,7 +192,7 @@ The `set` trap triggers when a property is written.
194192
- `value` -- property value,
195193
- `receiver` -- similar to `get` trap, matters only for setter properties.
196194

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`).
198196

199197
Let's use it to validate new values:
200198

@@ -535,7 +533,7 @@ sayHi("John"); // Hello, John! (after 3 seconds)
535533

536534
As we've seen already, that mostly works. The wrapper function `(*)` performs the call after the timeout.
537535

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:
539537

540538
```js run
541539
function delay(f, ms) {
@@ -609,9 +607,21 @@ Here are examples of operations and `Reflect` calls that do the same:
609607
| `new F(value)` | `Reflect.construct(F, value)` | `[[Construct]]` |
610608
| ... | ... | ... |
611609

612-
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.**
613623

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.
615625

616626
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:
617627

@@ -691,7 +701,7 @@ let user = {
691701

692702
let userProxy = new Proxy(user, {
693703
get(target, prop, receiver) {
694-
return target[prop]; // (*)
704+
return target[prop]; // (*) target = user
695705
}
696706
});
697707

@@ -710,15 +720,15 @@ Reading `admin.name` should return `"Admin"`, not `"Guest"`!
710720

711721
What's the matter? Maybe we did something wrong with the inheritance?
712722

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.
714724

715725
The problem is actually in the proxy, in the line `(*)`.
716726

717727
1. When we read `admin.name`, as `admin` object doesn't have such own property, the search goes to its prototype.
718728
2. The prototype is `userProxy`.
719729
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 `(*)`.
720730

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`.
722732

723733
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`.
724734

0 commit comments

Comments
 (0)