Skip to content

Commit 18e1d88

Browse files
authored
Various minor grammar fixes and improvements
Mostly adding "the" as necessary; typo fixes; some word order in clauses changed or contractions used for for smoothness
1 parent 646989d commit 18e1d88

File tree

1 file changed

+37
-37
lines changed

1 file changed

+37
-37
lines changed

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

+37-37
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ let proxy = new Proxy(target, handler)
1111
```
1212

1313
- `target` -- is an object to wrap, can be anything, including functions.
14-
- `handler` -- proxy configuration: an object with "traps": methods that intercept operations., e.g. `get` trap is for reading a property of `target`, `set` trap - for writing a property into `target`, etc.
14+
- `handler` -- proxy configuration: an object with "traps", methods that intercept operations. - e.g. `get` trap for reading a property of `target`, `set` trap for writing a property into `target`, and so on.
1515

1616
For operations on `proxy`, if there's a corresponding trap in `handler`, then it runs, and the proxy has a chance to handle it, otherwise the operation is performed on `target`.
1717

@@ -45,11 +45,11 @@ To activate more capabilities, let's add traps.
4545

4646
What can we intercept with them?
4747

48-
For most operations on objects, there's a so-called "internal method" in JavaScript specificaiton, that describes on the lowest level, how it works. For instance, `[[Get]]` - the internal method to read a property, `[[Set]]` -- the internal method to write a property, and so on. These methods are only used in the specification, we can't call them directly by name.
48+
For most operations on objects, there's a so-called "internal method" in the JavaScript specification that describes how it works at the lowest level. For instance `[[Get]]`, the internal method to read a property, `[[Set]]`, the internal method to write a property, and so on. These methods are only used in the specification, we can't call them directly by name.
4949

50-
Proxy traps intercept invocations of these methods. They are listed in [Proxy specification](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots) and in the table below.
50+
Proxy traps intercept invocations of these methods. They are listed in the [Proxy specification](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots) and in the table below.
5151

52-
For every internal method, there's a trap in this table: the name of the method that we can add to `handler` parameter of `new Proxy` to intercept the operation:
52+
For every internal method, there's a trap in this table: the name of the method that we can add to the `handler` parameter of `new Proxy` to intercept the operation:
5353

5454
| Internal Method | Handler Method | Triggers when... |
5555
|-----------------|----------------|-------------|
@@ -80,28 +80,28 @@ There are some other invariants, like:
8080
8181
Traps can intercept these operations, but they must follow these rules.
8282
83-
Invariants ensure correct and consistent behavior of language features. The full invariants list is in [the specification](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots), you probably won't violate them, if not doing something weird.
83+
Invariants ensure correct and consistent behavior of language features. The full invariants list is in [the specification](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots). You probably won't violate them if you're not doing something weird.
8484
```
8585

86-
Let's see how that works on practical examples.
86+
Let's see how that works in practical examples.
8787

8888
## Default value with "get" trap
8989

9090
The most common traps are for reading/writing properties.
9191

92-
To intercept the reading, the `handler` should have a method `get(target, property, receiver)`.
92+
To intercept reading, the `handler` should have a method `get(target, property, receiver)`.
9393

9494
It triggers when a property is read, with following arguments:
9595

9696
- `target` -- is the target object, the one passed as the first argument to `new Proxy`,
9797
- `property` -- property name,
98-
- `receiver` -- if the target property is a getter, then `receiver` is the object that's going to be used as `this` in its call. Usually that's the `proxy` object itself (or an object that inherits from it, if we inherit from proxy). Right now we don't need this argument, will be explained in more details later.
98+
- `receiver` -- if the target property is a getter, then `receiver` is the object that's going to be used as `this` in its call. Usually that's the `proxy` object itself (or an object that inherits from it, if we inherit from proxy). Right now we don't need this argument, so it will be explained in more detail later.
9999

100100
Let's use `get` to implement default values for an object.
101101

102-
We'll make a numeric array that returns `0` for non-existant values.
102+
We'll make a numeric array that returns `0` for nonexistent values.
103103

104-
Usually when one tries to get a non-existing array item, they get `undefined`, but we'll wrap a regular array into proxy that traps reading and returns `0` if there's no such property:
104+
Usually when one tries to get a non-existing array item, they get `undefined`, but we'll wrap a regular array into the proxy that traps reading and returns `0` if there's no such property:
105105

106106
```js run
107107
let numbers = [0, 1, 2];
@@ -122,11 +122,11 @@ alert( numbers[123] ); // 0 (no such item)
122122
*/!*
123123
```
124124

125-
As we can see, it's quite easy to do with `get` trap.
125+
As we can see, it's quite easy to do with a `get` trap.
126126

127127
We can use `Proxy` to implement any logic for "default" values.
128128

129-
Imagine, we have a dictionary with phrases along with translations:
129+
Imagine we have a dictionary, with phrases and their translations:
130130

131131
```js run
132132
let dictionary = {
@@ -138,7 +138,7 @@ alert( dictionary['Hello'] ); // Hola
138138
alert( dictionary['Welcome'] ); // undefined
139139
```
140140

141-
Right now, if there's no phrase, reading from `dictionary` returns `undefined`. But in practice, leaving a phrase non-translated is usually better than `undefined`. So let's make it return a non-translated phrase in that case instead of `undefined`.
141+
Right now, if there's no phrase, reading from `dictionary` returns `undefined`. But in practice, leaving a phrase untranslated is usually better than `undefined`. So let's make it return an untranslated phrase in that case instead of `undefined`.
142142

143143
To achieve that, we'll wrap `dictionary` in a proxy that intercepts reading operations:
144144

@@ -162,7 +162,7 @@ dictionary = new Proxy(dictionary, {
162162
});
163163

164164
// Look up arbitrary phrases in the dictionary!
165-
// At worst, they are not translated.
165+
// At worst, they're not translated.
166166
alert( dictionary['Hello'] ); // Hola
167167
*!*
168168
alert( dictionary['Welcome to Proxy']); // Welcome to Proxy (no translation)
@@ -225,7 +225,7 @@ alert("This line is never reached (error in the line above)");
225225

226226
Please note: the built-in functionality of arrays is still working! Values are added by `push`. The `length` property auto-increases when values are added. Our proxy doesn't break anything.
227227

228-
We don't have to override value-adding array methods like `push` and `unshift`, and so on, to add checks in there, because internally they use `[[Set]]` operation, that's intercepted by the proxy.
228+
We don't have to override value-adding array methods like `push` and `unshift`, and so on, to add checks in there, because internally they use the `[[Set]]` operation that's intercepted by the proxy.
229229

230230
So the code is clean and concise.
231231

@@ -292,11 +292,11 @@ user = new Proxy(user, {
292292
alert( Object.keys(user) ); // <empty>
293293
```
294294

295-
Why? The reason is simple: `Object.keys` returns only properties with `enumerable` flag. To check for it, it calls the internal method `[[GetOwnProperty]]` for every property to get [its descriptor](info:property-descriptors). And here, as there's no property, its descriptor is empty, no `enumerable` flag, so it's skipped.
295+
Why? The reason is simple: `Object.keys` returns only properties with the `enumerable` flag. To check for it, it calls the internal method `[[GetOwnProperty]]` for every property to get [its descriptor](info:property-descriptors). And here, as there's no property, its descriptor is empty, no `enumerable` flag, so it's skipped.
296296

297-
For `Object.keys` to return a property, we need it either exist in the object, with `enumerable` flag, or we can intercept calls to `[[GetOwnProperty]]` (the trap `getOwnPropertyDescriptor` does it), and return a descriptor with `enumerable: true`.
297+
For `Object.keys` to return a property, we need it to either exist in the object, with the `enumerable` flag, or we can intercept calls to `[[GetOwnProperty]]` (the trap `getOwnPropertyDescriptor` does it), and return a descriptor with `enumerable: true`.
298298

299-
Here's a working code:
299+
Here's an example of that:
300300

301301
```js run
302302
let user = { };
@@ -325,7 +325,7 @@ Let's note once again: we only need to intercept `[[GetOwnProperty]]` if the pro
325325

326326
There's a widespread convention that properties and methods prefixed by an underscore `_` are internal. They shouldn't be accessed from outside the object.
327327

328-
Technically, that's possible though:
328+
Technically that's possible though:
329329

330330
```js run
331331
let user = {
@@ -408,7 +408,7 @@ try {
408408
for(let key in user) alert(key); // name
409409
```
410410

411-
Please note the important detail in `get` trap, in the line `(*)`:
411+
Please note the important detail in the `get` trap, in the line `(*)`:
412412

413413
```js
414414
get(target, prop) {
@@ -420,7 +420,7 @@ get(target, prop) {
420420
}
421421
```
422422

423-
Why do we need for a function to call `value.bind(target)`?
423+
Why do we need a function to call `value.bind(target)`?
424424

425425
The reason is that object methods, such as `user.checkPassword()`, must be able to access `_password`:
426426

@@ -439,11 +439,11 @@ A call to `user.checkPassword()` call gets proxied `user` as `this` (the object
439439

440440
So we bind the context of object methods to the original object, `target`, in the line `(*)`. Then their future calls will use `target` as `this`, without any traps.
441441

442-
That solution usually works, but isn't ideal, as a method may pass the unproxied object somewhere else, and then we'll get messed up: where's the original object, and where's the proxied one.
442+
That solution usually works, but isn't ideal, as a method may pass the unproxied object somewhere else, and then we'll get messed up: where's the original object, and where's the proxied one?
443443

444444
Besides, an object may be proxied multiple times (multiple proxies may add different "tweaks" to the object), and if we pass an unwrapped object to a method, there may be unexpected consequences.
445445

446-
So, such proxy shouldn't be used everywhere.
446+
So, such a proxy shouldn't be used everywhere.
447447

448448
```smart header="Private properties of a class"
449449
Modern JavaScript engines natively support private properties in classes, prefixed with `#`. They are described in the chapter <info:private-protected-properties-methods>. No proxies required.
@@ -464,7 +464,7 @@ let range = {
464464
};
465465
```
466466

467-
We'd like to use `in` operator to check that a number is in `range`.
467+
We'd like to use the `in` operator to check that a number is in `range`.
468468

469469
The `has` trap intercepts `in` calls.
470470

@@ -495,7 +495,7 @@ alert(50 in range); // false
495495
*/!*
496496
```
497497

498-
A nice syntactic sugar, isn't it? And very simple to implement.
498+
Nice syntactic sugar, isn't it? And very simple to implement.
499499

500500
## Wrapping functions: "apply" [#proxy-apply]
501501

@@ -587,15 +587,15 @@ The result is the same, but now not only calls, but all operations on the proxy
587587

588588
We've got a "richer" wrapper.
589589

590-
There exist other traps: the full list is in the beginning of this chapter. Their usage pattern is similar to the above.
590+
Other traps exist: the full list is in the beginning of this chapter. Their usage pattern is similar to the above.
591591

592592
## Reflect
593593

594594
`Reflect` is a built-in object that simplifies creation of `Proxy`.
595595

596596
It was said previously that internal methods, such as `[[Get]]`, `[[Set]]` and others are specifiction only, they can't be called directly.
597597

598-
`Reflect` object makes that somewhat possible. Its methods are minimal wrappers around the internal methods.
598+
The `Reflect` object makes that somewhat possible. Its methods are minimal wrappers around the internal methods.
599599

600600
Here are examples of operations and `Reflect` calls that do the same:
601601

@@ -617,13 +617,13 @@ Reflect.set(user, 'name', 'John');
617617
alert(user.name); // John
618618
```
619619

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.
620+
In particular, `Reflect` allows us to call operators (`new`, `delete`...) as functions (`Reflect.construct`, `Reflect.deleteProperty`, ...). That's an interesting capability, but here another thing is important.
621621

622622
**For every internal method, trappable by `Proxy`, there's a corresponding method in `Reflect`, with the same name and arguments as `Proxy` trap.**
623623

624624
So we can use `Reflect` to forward an operation to the original object.
625625

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

628628
```js run
629629
let user = {
@@ -790,9 +790,9 @@ Many built-in objects, for example `Map`, `Set`, `Date`, `Promise` and others ma
790790

791791
These are like properties, but reserved for internal, specification-only purposes. For instance, `Map` stores items in the internal slot `[[MapData]]`. Built-in methods access them directly, not via `[[Get]]/[[Set]]` internal methods. So `Proxy` can't intercept that.
792792

793-
Why care? They are internal anyway!
793+
Why care? They're internal anyway!
794794

795-
Well, here's the issue. After such built-in object gets proxied, the proxy doesn't have these internal slots, so built-in methods will fail.
795+
Well, here's the issue. After a built-in object like that gets proxied, the proxy doesn't have these internal slots, so built-in methods will fail.
796796

797797
For example:
798798

@@ -806,7 +806,7 @@ proxy.set('test', 1); // Error
806806
*/!*
807807
```
808808

809-
Internally, a `Map` stores all data in its `[[MapData]]` internal slot. The proxy doesn't have such slot. The [built-in method `Map.prototype.set`](https://tc39.es/ecma262/#sec-map.prototype.set) method tries to access the internal property `this.[[MapData]]`, but because `this=proxy`, can't find it in `proxy` and just fails.
809+
Internally, a `Map` stores all data in its `[[MapData]]` internal slot. The proxy doesn't have such a slot. The [built-in method `Map.prototype.set`](https://tc39.es/ecma262/#sec-map.prototype.set) method tries to access the internal property `this.[[MapData]]`, but because `this=proxy`, can't find it in `proxy` and just fails.
810810

811811
Fortunately, there's a way to fix it:
812812

@@ -887,11 +887,11 @@ user = new Proxy(user, {
887887
alert(user.getName()); // Guest
888888
```
889889

890-
That said, the solution has drawbacks, explained previously: it exposes the original object to the method, potentially allowing it to be passed further and breaking other proxied functionality.
890+
That said, the solution has drawbacks, as explained previously: it exposes the original object to the method, potentially allowing it to be passed further and breaking other proxied functionality.
891891

892892
### Proxy != target
893893

894-
Proxy and the original object are different objects. That's natural, right?
894+
The proxy and the original object are different objects. That's natural, right?
895895

896896
So if we use the original object as a key, and then proxy it, then the proxy can't be found:
897897

@@ -932,7 +932,7 @@ A *revocable* proxy is a proxy that can be disabled.
932932
933933
Let's say we have a resource, and would like to close access to it any moment.
934934
935-
What we can do is to wrap it into a revocable proxy, without any traps. Such proxy will forward operations to object, and we can disable it at any moment.
935+
What we can do is to wrap it into a revocable proxy, without any traps. Such a proxy will forward operations to object, and we can disable it at any moment.
936936
937937
The syntax is:
938938
@@ -985,9 +985,9 @@ revoke();
985985
alert(proxy.data); // Error (revoked)
986986
```
987987

988-
The benefit of such approach is that we don't have to carry `revoke` around. We can get it from the map by `proxy` when needeed.
988+
The benefit of such an approach is that we don't have to carry `revoke` around. We can get it from the map by `proxy` when needed.
989989

990-
Using `WeakMap` instead of `Map` here, because it should not block garbage collection. If a proxy object becomes "unreachable" (e.g. no variable references it any more), `WeakMap` allows it to be wiped from memory together with its `revoke` that we won't need any more.
990+
We use `WeakMap` instead of `Map` here because it won't block garbage collection. If a proxy object becomes "unreachable" (e.g. no variable references it any more), `WeakMap` allows it to be wiped from memory together with its `revoke` that we won't need any more.
991991

992992
## References
993993

0 commit comments

Comments
 (0)