Preserve proxy invariants#16
Conversation
Preserve invariants specified in https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/get#Invariants Based on sindresorhus#15 Based on sindresorhus#8 Also add full support for accessor properties.
Use an explicit `typeof` test to decide whether a property value should be wrapped in a proxy. This is much more efficient than attempting to create the proxy and catching the exception. Based on some light benchmarking, the explicit test is as much as 40x faster.
Improve performance by performing the type test earlier in the `get` implementation. This avoids unnecessary calls to `Reflect.getOwnPropertyDescriptor`.
`Reflect.getOwnPropertyDescriptor` is moderately expensive and it is invoked on every access of a non-primitive attribute. Since they don’t change often, it makes sense to cache them. The cache is a two-level map of target -> property name -> property descriptor. The first mapping is a WeakMap so it will not interfere with garbage collection.
On `set` do a strict equality test between the previous and new value, and only fire the change callback if the value actually changed. As part of this fix, we also “unwrap” proxied values before assigning them to the target. This avoids “polluting” the underlying, wrapped object with proxies (which applications may access separately).
|
I've added a number of other functional and performance enhancements to this PR. If you'd like me to separate them, I can do that.. |
|
FYI here are the before/after benchmark results: Current: This PR: There is some noise in these results as I ran them on my laptop without really taking much care to ensure it wasn't busy with other processes. Nevertheless the performance improvement is clear :) |
Reorganize the benchmarks into a `native` and `on-change` suite for easier comparison. In addition, removed the loop in the `save` method. Since the implementation of the callback is outside the control of this utility, imo it makes sense to compare performance with a “no-op” callback. Proxies add a fair amount of overhead compared with native access, but the 1000-iteration loop was more or less overwhelming this overhead…
|
Hey! Nice stuff, I don't really understand what the proxyTarget stuff is supposed to do 😅 . I know @sindresorhus libs target Node, but is there a way we can avoid symbols? It's a non polyfill-able feature and not adopted by many browsers. |
|
There is no direct way to retrieve the I can switch to a string property name (like Looking at the browser compatibility for Symbol, it seems about as well-supported as Proxy itself. While there is a polyfill for Proxy, it doesn't appear to support all of the traps used by |
|
I’m not interested in making the code worse because of browsers. |
|
Ok I think it makes sense if they already have about the same support by browsers and it’s not a desired target of this lib. @kpruden ok now I get it thanks! I see that you unwrap the proxied value by doing |
|
Happy to explain.. In the |
|
Ohhhh I get it now! Thanks and super cool solution. |
|
@sindresorhus do you have any feedback on this PR? I'd love to get it merged... :) |
|
@kpruden This is looking really good. Thanks for elaborating on the implementation details. |
Preserve invariants specified in
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/get#Invariants
Based on #15
Based on #8
Also add full support for accessor properties.