Untrapped proxy properties

From time to time I'm using proxies to generate things on demand. It always bothered me the performance loss I get, especially on long proxy chains. I always cache the generated stuff in separate objects/maps, but invoking the handler methods still can cause a lot of performance loss in case your proxy is read frequently and it's executed on a hot path several times.

Here is an oversimplified example, I have chains that are 4-6 layers deep.

I simply want to be able to define untrapped properties on a proxy instance, which once are being set, they will never invoke the handlers again and it's executed with the same speed as a regular object access.

const proxy = new Proxy(...)

Proxy.defineUntrappedProperty(proxy, "color", "#000")

// This won't invoke any proxy handlers anymore
console.log(proxy.color) // #000

I believe this would be beneficial for a lot of libraries relying on proxies, accessing a property once would finalize its value, and wouldn't need to implement external caching solutions for computation heavy accesses.

they will never invoke the handlers again and it's executed with the same speed as a regular object access

We can specify "will not invoke the handlers", but we can't specify "executed with the same speed as regular object access". Engines have put a great deal of work and complexity into making regular object access fast, and they fundamentally cannot use that machinery for proxies. It would be a massive engineering effort to make them comparable in speed to plain objects even if there were a way to opt out of trapping specific properties.

Incidentally, you can approximate the thing you're asking for already:

let proxy = new Proxy(...);

let noTrapsForColor = {
  __proto__: proxy,
  color: '#000',
};

console.log(noTrapsForColor.color); // no traps, tada!

You can even have the proxy automatically add properties to the child object, if you want.

1 Like

You just made my day Sir. You have no clue how many weeks I wasted to come up with a viable solution, never thought about this "hack", it's smart.

I tried to play around with your solution a bit, but I immediately ran into an issue as soon I set up a set handler. By overriding __proto__ I simply cannot set properties anymore on the "fake" proxy which takes me back to square one.

If you have a set handler on the proxy, then you'll need to use defineProperty on the noTrapsForColor object to avoid triggering that handler.

Honestly, I didn't even try that, because on a regular proxy that also triggers set, and by overriding __proto__ I expected the same behavior. I did a few more tests, I had a case where my target was a function instead of an object, I had to do some magic there, but all worked out at the end.

Sorry to turn this thread into a SO Q&A session, I really appreciate your help!

We are looking to introduce a new integrity "trait" at the next plenary which would result in proxy traps no longer being invoked for cases where the proxy trap would have to answer according to the object invariants of its shadow target.

It should satisfy the use case of building objects lazily through a proxy while getting user code out of the way of normal object operations once built.

However the object invariants aspect would make this useful only for non configurable and non writable properties, not sure if that's acceptable for your use cases.