Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Proxy, a new JavaScript ES6 feature (atyantik.com)
199 points by tirthbodawala on May 18, 2018 | hide | past | favorite | 90 comments


A use case I had for proxies was a circular array type:

https://github.com/philbooth/hoopy

If the index overflows in either direction, loop back round to the start/end of the array. I wanted a circular array to use as a fixed-length buffer for receiving data in a streaming JSON parser I wrote:

https://github.com/philbooth/bfj/blob/61087c194d5675c75569a2...


A circular array type, love it! It looks like an excellent use case of proxies.


Technically the type should still be an Array with the backing implementation having a circular array behavior no?



Cool. I was seeing source of inner js, which in deed uses proxies to create structural sharing.

https://github.com/mweststrate/immer


I used proxies to make a fun library where you can call arbitrary functions, and it tries to make the function based on the name. For instance

  declaraoids.findWhereNameEqualsX(persons, {x: "Mats"});
It will generate the function on the fly.

https://github.com/Matsemann/Declaraoids


That's similar to Rail's dynamic `find_by` ORM methods where you can put the column name in the call:

    User.find_by_username "alice"


Haha that's a hilarious abomination :)

It's a shame it doesn't have declaraoids.makeMeASandwich() on the roadmap though.


You should submit a pull request.

In fairness, some RPC web frameworks do this in a completely non-ironic non-joke way. CakePHP "findBy" comes to mind.[1]

[1] https://book.cakephp.org/2.0/en/models/retrieving-your-data....


Also Java's Spring Data:

https://docs.spring.io/spring-data/commons/docs/current/refe...

Wasn't it an ActiveRecord thing to begin with? Spring got a lot of its more recent awful features by trying to copy Rails.


And somewhat ironically Rails deprecated the particularly magical features like `find_by_name("Bob")` aliases in favour of saner variants like `find_by(name: "Bob")`.


What a great change.

It always seemed ridiculous to put the lookup key and lookup value in two different places.

Nice to see Rails reconsider these things I once thought it would never surrender.


Ruby didn't have keyword arguments when they came up with the initial approach.


That's not why they did it.

For example, they could've used maps:

    find_by name: "foo"


Also the Grails ORM, but that's probably taken from Rails as well. I actually really liked it back when I was using it. Is there a good argument why automated function synthesis is a bad thing? Not trying to argue, just curious.


It's not type safe, so if you change a property name it's not going to be picked up at compile time, so you'd better have tests in place for every synthesised function. (Of course, most implementations are made in dynamically typed host languages, which have this problem with every line of code.)

It's also just a limited DSL that has none of the capabilities of the host language, and can't be used to express e.g. complex queries.


Yeah in general this approach is based on ActiveRecord from Ruby On Rails.

But in Ruby it's based on method_missing not a proxy. I believe ruby got it's method_missing idea from Smalltalk. But there might be other precursors.




As a Python guy, where we have __setattr__, __setitem__, etc., I was at first wondering why you would use a proxy for such a thing. Better make it part of the language.

But the more I think about it, the more I can see that it's handy to have an official proxy interface for this, for the case of an external code needing to wrap your object.

Also, I've seen so many reinvented wheels for proxies, and usually bad ones, as it seems an easy problem to solve, but there are plenty of edge cases. Proxies avoid the recursive problem altogether.

Besides, __dundermethods__ tend to make the language hard to optimize.

Still, it's really overkill for simple cases like the ones addressed by Object.defineProperty (or @property for Python), as you create a huge layer of indirection for the entire wrapped object. So if you use such a Proxy, you better have a damn good reason.


I think it's a matter of performance. Compiler / interpreter / runtime will only add overhead in lookups for this objects explicitly matched as proxy, but in normal case it can go directly to the cached functions.


This is part of the language not just a an interface (though I'm not sure it is an interface, I think it is an object primitive). You wouldn't be able to write this using plain Javascript. Which is why there are no polyfills for client-side code.

Incidentally, PHP does it a similar way to Python with so-called "magic methods" but I can see the benefit to doing it this way.


I’d rather read an article that doesn’t focus on the API (which I can look up on MDN), but rather on the practical use cases (which are merely a footnote in this article ).


I agree: get to production uses with code examples as quickly as possible.

Tough to know whether this is a fringe feature or actually useful on a regular basis without really digging in. Luckily, other comments here highlight some usecases.


Proxies can also be really helpful for unit tests.

Obviously you have things like sinon for stubbing out methods, but they do have to modify the original object, which I don’t think anybody loves.

But you can also wrap your object in a Proxy with handlers that stub certain methods.

You can also use proxies to implement validation and observability on objects. Here’s a very minimal example of a sort of Backbone-esque Model class with proxies: https://gist.github.com/kevincennis/25ba31b2e9f9c8b7a34047ba...


Salesforce use this in their Lightning Framework. I've not gone beyond the surface of that framework, but I've found its use of proxies adds friction to development. Trying to debug in the browser, anything other than a primitive results in a looking at an empty Proxy object - I believe this is a deliberate choice behind the framework in the name of security. But it makes it much harder to discover what's in an object - if you don't know the name of a property, good luck getting it.

Note that this is either due to how the framework uses Proxy, rather than Proxy itself, or I'm doing it wrong and should do more reading up on it. It really could be the latter - given my level of experience and knowledge, I thought hard about posting this comment!


Everything about the Salesforce JS experience from the ground up is horrible. They'd have to pay me a lot more money to do that again.


The biggest thing that gets me when my coworkers do SF dev is how many times they have to refresh to see the new code. Its almost always two or three.

Ive seen the proxy thing trying to debugging. I didn't realize it was a set pattern/language feature.


This is new to me, and it took me a while to see the point, but having read the Mozilla docs (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...) I can see a few use cool examples of validation and input correction. That said- all of these things _seem_ to be fairly trivial to achieve without proxies, so I am wondering- what is the killer use-case that you absolutely positively need js proxies for?


You can use them to call some functions when an object property is called or set. P.S. Just like Database triggers ;).

You can use them conditionally to manipulate data before fetch or before storing. For example: you want to store the username when a person registers using the email id like userzyx@gmail.com then you can use it to generate username from it by truncating the later part and generate unique username which will be "userzyx" here.

Just open up your brains and see where further it can be used and possibilities are just unlimited.


You are essentially restating my question: what _are_ the possibilities with proxies? (What can you do with proxies that is much more messy/difficult/verbose to do otherwise?)


One possibility is adding syntactic sugar to objects. I posted elsewhere in this thread about how we used them in Remote Browser, but here's a simple example of adding support for negative indexing to an array. This basically creates a shorthand for array[-n] === array[array.length - n].

    class MyArray extends Array {
      constructor(...args) {
        super(...args);
        return new Proxy(this, {
          get: (target, name) => {
            if (name && name.match && name.match(/^-\d+$/)) {
              return Reflect.get(target, this.length + parseInt(name, 10));
            }
            return Reflect.get(target, name);
          },
          set: (target, name, value) => {
            if (name && name.match && name.match(/^-\d+$/)) {
              return Reflect.set(target, this.length + parseInt(name, 10), value);
            }
            return Reflect.set(target, name, value);
          },
        });
      }
    }
You could accomplish the same thing by creating explicit setters and getters, but proxies allow you to directly intercept and modify the behavior of property access, calling an object like a function, etc. This sort of flexibility allows you to create APIs that wouldn't be possible otherwise.


I've used it to extend the functionality of a library without having to monkeypatch or subclass a bunch of classes. See here: https://github.com/StephenGrider/AdvancedNodeComplete/blob/m...

The CustomPage class 'extends' through the use of a proxy a Page and Browser class, while adding its own methods on top. The proxy mediates access to the Custompage instance, and the Page and Browser. When calling a method on CustomPage, it first looks to see if the method exists on the CustomPage instance, then the Browser instance, then the Page.

I had originally wanted to subclass Page, which is implemented by the Puppeteer library, but the Browser class had a dozen direct references to the Page class. I would have had to sublcass the Browser as well and override all those methods to instead reference my CustomPage. The proxy seemed like an appropriate solution.


You can log all property accesses on an object, e.g. for testing purposes. For example, see https://searchfox.org/mozilla-central/rev/da499aac682d0bbda5... (or at https://github.com/w3c/web-platform-tests/blob/a47dd526b9033... depending on which of these stays up more permanently).


Here's a pretty good example of how a proxy can be used from Node.js source code: https://github.com/nodejs/node/commit/f074612b744fecfd1f79fd...


https://github.com/solkimicreb/react-easy-state

pretty cool library that is only possible because of es6 proxys


I use this for sql queries:

  const user = await db.connect(sql => sql.getUserById(43))
The sql value is a proxy that looks for a file called 'getUserById.sql', reads sql out of it, sends the query to the database, and returns a promise for the result. With caching and some sugar methods, it's a pleasant way to use a database.


I was talking about this with a friend and he remarked that this could lead to some pretty opaque code where things behave differently from what you'd usually expect. While I think this is a cool feature, I think I agree with him that you probably want to think twice before using Proxies.


I used object proxy to create a DSL for constructing object factories, inspired by Ruby's FactoryBot https://github.com/nathanstitt/object-factory-bot

It's pretty handy for generating data for unit tests, but wouldn't recommend it for production code since I've discovered Proxy is fairly slow.


Anyone using redux, I created this using proxies - https://github.com/alnorris/redux-actiontyper


Why is this an ES6 feature? Isn't ES6 set and implemented? Wouldn't this be part of a future spec (ES2018, ES2019, or whatever)?



Because it is not a future feature, it exists for years now (I first used it in 2016).


It's an existing, rarely-used feature. You can try it in your browser!


It's a poor title. It's not your fault.


The use of `document.write` for this entry instead of the usual console threw me off at first.


Presumably so you can see the output in the embedded JSFiddles.

FYI, if you're embedding a JSFiddle you could also just drop this in the HTML section then use console.log:

    <script>console.log = function(str) { document.write(str, "<br/>") }</script>


JSFiddle should just have a tick box with "intercept console.log" and a tab with the output .


Hahahaha...It should!


I feel as though this is a missed opportunity to use a proxy!


I initially stared at that with horror, but I suspect that Stack Overflow does something similar with their code snippets. If you write console.log it gets output to the screen [0]

[0]: https://stackoverflow.com/a/49968603/327074


It is just because since all the JSFiddles are on one page. All the console.log would be together in the console and it would be difficult to know which console is for which code.

Hence document.write is used.

Though HTML could be used but that would just increase one more tab to show useless HTML. ;)


This is another feature that goes into bucket "I'll never use" along generators


Generators require a mind switch to be used regularly. And the browser is not the place where the most obvious use cases are, so it's harder to make your first steps here.

The easiest things to start using generators is to write sysadmin scripts that parse big files. Once you've done that, you get the memory benefit. Then the more you use it, the more you get the lazy evaluation benefit and you start using it elsewhere.

It's clearer in Python because:

- generators have there for ever

- we have unified way of iterating on things, and generators just work transparently

- you can start learning generators by just changing [] to () in your comprehension lists

So in JS it's much less obvious.


Yeah, lazy evaluation is the big feature of generators. Given that it's such a powerful feature of Haskell it's definitely worth investigating them.


I agree it doesn't seem super useful, but it can be used to pull some semantic tricks. I used it in [Rebridge](https://github.com/CapacitorSet/rebridge/) to implement syntactic sugar for database access, so that accessing a property on an object translates transparently to a query, and I've seen it used to [allow for typos in property names](https://github.com/mathiasbynens/tpyo).


I especially liked the "construct" part as we can use it to take input from the users in frontend as a numbered value and then store it in the database with the currency format which can be later used to make the value visible with the currency in the frontend.


It's such a niche feature. And only useful if you use mutative OO style. Also it gets a bit too much into the magic territory for me, this is hard to debug and reason about, even harder than getter/setters.


It's a feature you'll probably end up using all the time without even noticing it.

I just wrote a class to keep track of changes to an object using proxies yesterday. That way you can pass an object to a form, modify it in place and then roll back if needed. Also makes it easy to get deltas and only post the updates to the backend.


Well technically if you use async / await, it's built on top of generators and promises.


Proxies are a basic feature that should be a part of any OO language. Their usefulness ranges from "save yourself a lot of repetitive work" to "impossible to implement without".

Very simple real-life use case: factor out logging and move it outside of normal logic of the object.


Here's a list of stuff proxies have been used for https://github.com/mikaelbr/proxy-fun


I can see a very valuable use case for being able to intercept method calls and attribute lookups to do input validation, logging, etc. But being able to dynamically decide which attributes exists sounds like a nightmare from a code-analysis perspective.

Python has had the __getattr__, __setattr__ and friends and once you start using them to be "creative" all static type checkers break down. They are usually frowned upon.

Let's hope typescript is able to interpret these Proxy objects and that the community don't abuse them for all kinds of strange things.


Big win for JavaScript DSL composers. Reminds me of method_missing and other sharp knives in ruby.


I implemented JavaScript Safe Navigation using proxies as an experiment to learn more about proxies.

This is similar to the TC39 Optional Chaining Proposal https://github.com/TC39/proposal-optional-chaining

https://gist.github.com/dakaraphi/6a87168db66fd8f032d2


This makes sense to me - it feels like an interface somewhere in the space of Python's properties (using the @property decorator), its descriptor protocol[0] with which properties are implemented, and its system of magic methods like __getattr__ and __getattribute__.

I am a bit intrigued about the way it's separated from the "nature" of the underlying Object though - in Python, we'd have a single canonical interface to the data, bound to the class in which the data was instantiated. Here, in ES6, it seems like the data might be attached to a base Object, and you could use that Object with any number of different proxies to provide polymorphic behavior.

Overall, these seem like very different philosophies of object-oriented programming. Which I guess makes sense given how the languages have evolved over time.

[0] https://docs.python.org/3/howto/descriptor.html


https://caniuse.com/#feat=proxy

Seems to have a decent coverage for most users


The issue is that for many applications anything less than ~98% is not acceptable (eCommerce for instance). This is 87% (to save people a click).

And because this works on fundamental operators, a polypill is impossible.

However, if you are using Babel to transpile your JS or using Node.js this seems like it could be very useful.


You also cannot transpile Proxies, since they do not have an equivalent in "old javascript" as they are a feature in such a level that cannot be built with previous tech.


You can transpile pretty much anything. The transpiler doesn't have to just modify the declaration, it can also modify the calls...

https://www.npmjs.com/package/babel-plugin-proxy

> they do not have an equivalent in "old javascript"

Sure they do. Function calls.


Ah totally, I misspoke. I was talking from the point of view of a library author, where I'd like to transpile my code so anyone can include the library and use it. In the same way I can use const, transpile my code and no one worries about me using const internally, the same cannot be said for Proxy.

So, between everyone having to transpile their client code or losing the few devs who need to support IE, I prefer the latter for my projects.


I'm not really a fan of these "magic" code creators. I'd use it only as required and not all out.


Proxy is nice but the scope is very limited if you tend to avoid metaprogramming.

One use I've found is for library maintainers - a way to expose a large API without taking a hit for requiring the whole thing. We use it to consume parts of that API without having to require all the dependencies and subdependencies.


Proxies are awesome. I used them to create observables that automatically publish on any mutation, no matter where in the object tree it happens: https://www.npmjs.com/package/mutable-model


Cool stuff, I made a more general use library that notifies you of any chance on an object property, no matter how nested it is: https://github.com/alessioalex/recursive-object-proxy


This isn't actually a new feature, but at least the thread is giving it some more exposure. It's been implemented on many browsers for some time already. I've for example used it for doing quick data-binding. It's really useful and more people should know about it.


Proxies are hardly new (2014?), well, just like Es6. There are some interesting applications [0] but I don't know if is use it in production.

[0]: https://curiosity-driven.org/array-slices


> Data persistence. We add a proxy backup function to each object that is fired when modifying its content.

This makes me think of database triggers, which would imply that it's quite easy to implement dark patterns with Proxies.


I made a simple library to add ruby's `method_missing` to Javascript: https://github.com/ramadis/unmiss


Was looking for something exactly like this the other day! No idea this existed, this will especially be great while stubbing/mocking in tests.


Proxies are definitely fun. We use them extensively in our browser automation framework, which is largely based on the Web Extensions API [1], to implement syntactic sugar in the interface. The general goal behind Remote Browser [2] is to make it really easy to execute privileged code in a web browser, but to otherwise stay out of your way as much as possible. This is accomplished through the heavy use—some might even say abuse—of proxies.

For instance, take a look at this code example of opening a tab.

    await browser.evaluateInBackground(createProperties => (
      browser.tabs.create(createProperties)
    ), { url: 'https://intoli.com' });
The browser.evaluateInBackground() method is part of the Remote Browser API, and it simply evaluates code in a background script context in the browser. The browser.tabs.create() call, on the other hand, is part of the Web Extensions API. The vast majority of Remote Browser's power is provided through remote code execution and the Web Extensions API, so we really wanted to provide a more concise syntax for performing this sort of operation. Using proxies, we were able to make the browser object directly callable as a shorthand for background context evaluation. This code example is exactly equivalent to the previous one.

    await browser(createProperties => (
      browser.tabs.create(createProperties)
    ), { url: 'https://intoli.com' });
That's a bit more concise, but the real sugar is in the next step of abstraction. Instead of explicitly evaluating a function in the background script context, you can simply treat the browser object in the client context as though it were the Web Extensions API browser object in the background script context. This code example is again exactly equivalent, and again accomplished using proxies.

    await browser.tabs.create({ url: 'https://intoli.com' });
The tabs.create method isn't hardcoded into the Remote Browser library. Instead, proxies are used to evaluate any non-local API calls in the background script context. This means that you automatically get full access to the version of the Web Extensions API that your browser supports, whether you're using Chrome, Edge, Firefox, or Opera.

The one last piece of syntactic sugar is the shorthand for evaluating code inside of individual tabs. The syntax is very similar to how you evaluate functions in the background script context, you just need to first use square brackets to indicate the tab ID.

    await browser[tab.id](createProperties => (
      document.getElementById('some-link').innerHTML
    ), { url: 'https://intoli.com' });
This is also implemented using proxies. In fact, the same trap that handles the local Web Extensions API calls handles the tab access, and it differentiates between the two depending on whether or not the property name consists entirely of integers. In both cases, it returns another proxy that is able to handle either additional chaining or function evaluation.

Anyway, sorry that this ended up a lot more long-winded than I was expecting. If this has piqued your interest in Remote Browser though, we made an interactive tour of the project that you should check out [3]. You can actually launch and control browsers from inside of your own browser in the tour, and it explains a lot more about the project philosophy and what you can do with it

[1] - https://developer.mozilla.org/en-US/Add-ons/WebExtensions/AP...

[2] - http://github.com/intoli/remote-browser/

[3] - https://intoli.com/tour


I liked the functionality but found it hard to implement. Not a good sign.

Successful features are those that surprise in the easy direction (arrrow functions) rather than in the hard direction (proxies).


What were your problems? I on the other hand found it very intuitive.


Can’t remember. Maybe I was misusing it. Probably I was trying to either prevent access to object methods where user not authenticated or prevent access where object not initialized.


So this is sort of like metatables in lua?


Except that metatable may change the behavior of a table itself, and js proxy is just a wrapper that cannot change an original object.


ah thanks for clearing that one out


What is metatables?


Does es6 tags use this under the hood?


Unfortunately IE doesn't Proxies them at all and you can't even shim support for them on IE.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: