r/javascript 9d ago

JavaScript Magic Objects

https://noeldemartin.com/blog/programming-patterns-magic-objects

I just published a blog post to my website about a pattern I've been using for a while that I think you will find interesting (and probably very divisive!): Magic Objects.

class Parrot extends MagicObject {
    __get(property) {
        return `${property}! ${property}!`;
    }
}

const parrot = new Parrot();

console.log(parrot.foo); // "foo! foo!"
console.log(parrot.bar); // "bar! bar!"

Basically, I'm borrowing the idea of Magic Methods from PHP and porting them to JavaScript. You've probably seen something like this done with proxies before, but I still like to use some OOP in my JavaScript, and I also came up with a way to make it work nicely with TypeScript.

It may seem a bit over the top at the beginning, but I've been using this for a while and I've found it very useful.

Let me know what you think!

11 Upvotes

46 comments sorted by

View all comments

34

u/javascript 9d ago

Proxy Objects (getters and setters) have been around for a while but I've yet to come across a use case where they were the right choice. They tend to work more as an escape hatch.

13

u/jhartikainen 9d ago

It seems potentially valuable in some kind of framework/infra code. Svelte uses them for its $state runes for example. But yeah, like generators, I've not really found any use for them myself.

4

u/Dragon_yum 8d ago

I worked with JS/ts for nearly a decade and only came across one time where a generator was the right call

5

u/javascript 9d ago

Oh ya generators haha. I was so bewildered by the `function*` syntax when I first learned it. What a wild land grab for such a niche feature.

3

u/alex-weej 9d ago

It's not that niche! Could have been a keyword though.

1

u/javascript 9d ago

What have you used generators for?

6

u/Kwantuum 8d ago

Generators were used to transpile async/await for years.

I've used generators on a number of occasions. Generally I avoid it unless it's a great fit for the problem because they remain a relatively obscure feature for most people. They're a great way to get support for for..of on custom containers, among other things.

The other problem with generators and iterators in general is the lack of standard library support for manipulating them. No standard iterator mapping/filtering really reduces their usefulness.

2

u/svish 8d ago

Generators are awesome, but unfortunately handicapped still. Not being able to filter, map, reduce, and so on directly on a generator makes them annoying and the moment you have to put all the items in a fixed length array, you kind of miss several of the advantages.

2

u/sharlos 7d ago

I think they've added sync versions of map, filter, etc, with async versions on the way.

1

u/svish 7d ago

Yeah, think they're working on it, but really, it should've come at the same time as generators themselves.

1

u/vincentdesmet 9d ago

yeah, i’ve used them as wrappers around CDKTF to intercept and convert attribute getters into creating Terraform remote state lookups

fun, but confusing (leaky abstraction) for DX

1

u/Embostan 3d ago

SolidJS 2.0 has found a nice use for generators

5

u/ic6man 9d ago

I used a proxy object around a pre-existing API implementation to provide retry and consistent error handling + short circuit.

I don’t know that I would call that an escape hatch. It’s just a nicer way of building on top of something you don’t have access to modify yourself.

2

u/javascript 9d ago

I would absolutely call that an escape hatch. And a very useful one to have available!

5

u/aknavi 9d ago

love your username :)

3

u/javascript 9d ago

I love yours too! "AK Navi" your one stop shop for Navigating Alaska!

2

u/lookarious 9d ago

Libraries like MobX uses proxies, its useful, but they are not good as in other languages, for example there is no way to use custom setter for whole class because JS is dynamically typed.

1

u/hyrumwhite 8d ago

I like using them to proxy web worker messages, so invoking a web worker method is just doing myWorkerProxy.doBigJob() with type support derived from the actual handler object in the worker. 

1

u/javascript 8d ago

Sounds like an escape hatch to me haha

1

u/kilkil 7d ago

omg its him. John Javascript

0

u/TheThingCreator 9d ago

In my opinion they are not useful in normal development, they are the right choice if you're building a framework and need reactivity basically. Even "magic methods" don't sound like the right choice unless you're making a framework. Using these kinds of things just adds ambiguity to your code but they are highly useful for framework development.

2

u/javascript 9d ago

As a former library developer myself, agreed. Sometimes to make a truly generic interface, you need niche features. Glad they're available! But not a good default haha

1

u/noeldemartin 9d ago

Yes, I agree. In fact, I've only used them in 2 projects so far: an ORM and a Vue framework. Both libraries.

However, with the right Typescript declarations, it's not that ambiguous from user land. For example, in my ORM I define the fields with Zod, and using an IDE you can go straight to the Zod definition every time you use the field. They aren't declared as "any".

In any case, it's definitely a "sharp knife" and not something I use too often. But I specially like the hack of returning a Proxy from the constructor :). I haven't seen this used anywhere else, but IMO it enables some pretty powerful patterns.

1

u/TheThingCreator 9d ago

Ya I used the proxies to make a reactivity module for an app, it works very nice but ya things can get pretty pointy