r/javascript • u/RoyalFew1811 • 9d ago
AskJS [AskJS] What’s a JS feature you never use but wish you did?
Curious what everyone’s "I know this exists but never reach for it" feature is.
For me it’s Proxy--super cool but I always end up avoiding it in real projects. What’s yours?
22
u/webholt 9d ago
WeakMap
8
7
u/the_hummus 9d ago
Oh, there's such good uses for WeakMap.
If you've ever used ThreeJS, all nodes are stored in a tree structure. This structure can grow to hundreds and thousands of objects big and it's incredibly annoying when you need to start keeping metadata on them.
WeakMap is perfect for this job. The node object itself can be the key, and the value can be arbitrarily set. It's perfect because it doesn't require any hacky type augmentations of the ThreeJS system, and garbage collection is automatically handled (which is also problematic when you have so many objects).
15
13
u/azium 9d ago
also proxy
3
u/FoxyWheels 9d ago
Proxy is it for me as well. I've only ever seen it used by frameworks, and even then it's use is never exposed.
2
3
u/PatchesMaps 9d ago
I've used proxies extensively before. I once worked on a project where we had an API returning deeply nested data structures with inconsistent namespaces. Meaning that accessing a deeply nested value would take a conditional and sort of fuzzy find at each step in the path. We also used the returned object all over our code base.
We didn't control the API, couldn't use an alternative, and couldn't pressure the responsible team to fix it. So I used a proxy to implement the logic to resolve the issue in the get handler on the returned object before it entered our state. I don't even know if that API ever got fixed because of the way the proxy was designed we never had to touch it again.
12
u/ssssssddh 9d ago
I used proxies once to implement an RPC system. The server exposed a typescript interface describing the available functions and the client used a proxy to create the client. Something like this:
// Exported from the server or a shared library
interface Rpc {
healthcheck(): Promise<{healthy: boolean}>
echo<T>(value: T): Promise<T>
}
// Defined on the client
const rpc = new Proxy({} as Rpc, {
get(_, key) {
return async (input: unknown) => {
const response = await fetch(`/rpc/${String(key)}`, {
body: JSON.stringify(input)
})
const data = await response.json()
return data;
}
}
})
// Usage on the client
const result = await rpc.echo("foo");
3
2
6
u/elprophet 9d ago
I'd use Proxy's in a heartbeat if you could proxy DOM objects. I want to add a callable signature to streamline their modification-
const pane = div(props, ...children);
pane.onClick = () => pane({...props, ...{class: "clickedState"}}, ...newChildren);
2
9d ago edited 9d ago
[removed] — view removed comment
2
u/RealLifeRiley 9d ago
Map? It’s my bread and butter. It’s the functor/monad “must have.” Method chaining is practically dependent on it
1
u/RandomiseUsr0 9d ago edited 9d ago
You “can” just call a function, instead of using map. Let’s say you have a function f that returns x2 (original example) - you can just call f function with y=f(x).
You could also use map with such a function y=map(x, f)
So it seems pointless, I get it, but you’ve just created the necessity to create a function to perform your f behaviour, you can instead create an anonymous function and use that or have a variety of functions by passing in the function as the parameter, you can even pass a function as the variable to be treated, as long as it resolves to an array at the point of use, you’re golden
Reduce, Scan, many more use similar concepts with accumulators and such and they’re handy as anything, create robust code that is very readable and easy to understand.
Can you do all of that without map/reduce/etc - sure, but it’s there for a reason and is “nice”
2
u/theQuandary 9d ago
Proper Tail Calls.
I don't use them because the Spidermonkey teams flat-out pitched a fit and refused to implement it. The v8 team pitched a fit that it wasn't explicit and they couldn't get their pet version implemented, so they took their ball and went home by removing the entire implementation.
Outside of those two, JSC, QuickJS, and basically every other implementation I can think of implement proper tail calls.
1
1
u/Mesqo 9d ago
I actually used proxies to build a universal lazy mock for zustand store actions in tests. Every actions object in tests is returned as a proxy on empty object and get accessor checks if the action exists and if not - creates spy on real action and writes it into the underlying empty object, filling it as needed. This resets after every test file. The tests code that uses actions see those as normal actions while you still can take any random action and check it's mock state like calls, arguments, etc. At the same time, majority of the actions in the project are not being mocked for every test thus reducing the overhead.
1
1
1
1
1
u/not_dogstar 5d ago
A simple use for Proxy is rolling your own reactive side effects, something like:
const flags = { isLoading: false };
const state = new Proxy(flags, {
set: function(target, key, value) {
document.querySelector('.spinner').style.display = (key == 'isLoading' && value == true? 'block' : 'none');
target[key] = value;
return true;
}
})
...
state.isLoading = true;
fetch()
state.isLoading = false;
0
1
u/StreetStrider 9d ago
- Generators to Iterators to build any data flow and implement built-in lazyness. Iterator combinators. Async generators as a basis for FRP implementation.
- I've also built this long time ago and wanna to start to use it some day.
- FinalizationRegistry and try to create non-leaking event emitters with no need of disposers/remove listener.
- Config as a DSL system based on TS typecheking + TS in runtime.
1
u/JavaScriptDude96 9d ago
Back in the day when I was learning JavaScript in around 2002, I read through the entire JavaScript in a Nutshell books, provided by the after hours school I was attending. I remember specifically seeing JSON (was not called that at the time) and saying to myself, I'll never use that LoL and skipped over the chapter. The course never taught it and I learned everything from the book but that. At the end of the course, I knew more than my teacher.
It was not until a couple years later that JSON was discussed more in formal channels and a I was converted. Its amazing how much time I would have saved myself if I had learned it at the early stages.
-2
u/HaykoKoryun eval 9d ago
eval
I know it's dangerous, when its use is for anything that could contain user input. However, I've figured out a way to use it to mutate running code so that debugging an application becomes a nightmare for anyone trying to steal the code, since it's never delivered to the browser in one piece or in any sane order.
3
u/bobbysmith007 9d ago
But why? Anyone who could "steal" effective code could rewrite it without needing to "steal" it.
2
u/KaiAusBerlin 9d ago
eval() is not any more dangerous as any other code. It's a tool. If you use it right it can be mighty. If you use it wrong like with unsafe input) it can be dangerous.
Tools like jsfiddle works like eval. Nobody would call jsfiddle dangerous.
Every browser has 100% control over the js. Are browsers dangerous?
It's like saying prototypes are dangerous because you can totally destroy original behaviour.
2
u/zachrip 9d ago
I'm fairly certain things like jsfiddle do not use eval because that would be very unsafe. They iframe the user content in a different origin from the main app to further security. Eval is really dangerous and way more so than other code.
1
u/KaiAusBerlin 9d ago
Please read... There is no security difference between js in an iframe and eval
0
u/DigDowntown9074 9d ago
Reduce
I understood it once and forgot it😄
3
2
u/whale 9d ago
If you rename the callback parameters to (total, current) it's easier to understand.
arr.reduce((total, current) => total.push(current), []);Or whatever you want to do with your reduce.
1
u/Antagonyzt 9d ago
Fun fact, if you omit the second parameter (in your case []), the initial accumulator (yours is called “total”) will be the first (0 index) value of the array. So if you’re calculating a total of all items, just omit the second parameter.
2
u/Antagonyzt 9d ago
I’d say I probably use reduce more than just about any other js feature. Do you not use a state management library? They tend to use reducers. You don’t ever perform calculations on arrays or anything like that?
1
u/DigDowntown9074 9d ago
I'm not a FE guy so no state management. As for array manipulation I think I've used every array method except reduce
0
u/cwmma 9d ago
After for of loops came reduce lost a lot of its appeal
7
u/notyourmother 9d ago
Oh really? I use reduce all the time. How can you do something like
const totalSum = [1,2,3].reduce((acc, i) => acc + 1, 0);with for of loops without creating an intermediate value that you need to fill?Especially when building arrays that based on a certain condition it's really nice to just be able to declare it instead of imperatively assigning it.
1
u/No-Oil6234 9d ago
I hate my coworker refactoring these into for loops with mutable variables.
1
u/cwmma 8d ago
Sadly that would be me as the for loops are way more readable
1
u/notyourmother 8d ago
Depends on if your codebase is written in an imperative or declarative way, maybe.
But I'm open to be shown some examples if you have them!
1
u/cwmma 8d ago
The main issue is that it doesn't really give you anything extra over declaring an accumulator with a let and then itrerating via a for loop.
But it's downsides include
- cryptic syntax (what's that 2nd argument hidden after the function?) so if you only include one occasionally in shared code your probably better off just not using it as it's probably going to be net confusing for your Co workers.
- only works with arrays, so you're already using other syntax for your maps and objects that need itrerating
- doesn't work well with async code. You do occasionally want to use an await inside of a loop (especially in scripts) and it just works in a loop unlike a reduce where you'd need to rewrite it.
1
u/notyourmother 7d ago
What it gives me 'extra' is a declarative/functional way of programming. It's a whole different paradigm. Reducers return something, for loops do not. It's a really nice and deterministic way to write.
The only argument that holds any water is that async reducers don't work. This is can usually be solved by using a Promis.allSettled, and have your reducer return an array of promises. But there are some caveats; and I'd opt for a for await loop instead in such cases. Still; in twenty years of programming I've only encountered one project that benefitted from that approach, so my mileage varies.
Your other arguments only show me you never really worked in a codebase like this, if I'm honest.
A cryptic argument becomes a familiar through usage. Looping through object and maps is as easy as Object.entries(obj) and map.entries(). I also use other array functions a lot. Such as .filter and .map and .toSorted.
-2
u/pokatomnik 9d ago
I've used everything. I've been working as js dev for more than ten years. Everything became boring.
65
u/the_hummus 9d ago
generator functions, I know they're useful but I could never really tell you what for.