News for hackers. Stuff that matters.
Re:How JavaScript Works: deep dive into call, apply, and bind by cj on Friday July 09, 2021 @06:05PM (#27789052)
> should be discarded and forgotten about, they add too much confusion

How does it add confusion?

Bind, call and apply are fairly straightforward and not difficult concepts to grasp for any mid to advanced level JS dev and they're useful in many scenarios (although bind is less useful / less needed now that we have arrow functions, but call and apply are definitely not something to be "discarded").

Re:How JavaScript Works: deep dive into call, apply, and bind by Arnavion on Friday July 09, 2021 @06:19PM (#27789162)
`call` and `apply` can also be replaced with arrow functions and splats.
Re:How JavaScript Works: deep dive into call, apply, and bind by hajile on Friday July 09, 2021 @06:23PM (#27789193)
How would you replace the classic `Array.prototype.map.call($nodeList, myFn)` with arrows and splat?
Re:How JavaScript Works: deep dive into call, apply, and bind by lhorie on Friday July 09, 2021 @07:41PM (#27789708)
> The Array.from() static method creates a new, shallow-copied Array instance from an array-like or iterable object[0]

So, do this instead:

    Array.from($nodeList, myFn)
[0] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
Re:How JavaScript Works: deep dive into call, apply, and bind by merrywhether on Saturday July 10, 2021 @12:16AM (#27791071)
I never knew this mapping variation of Array.from, that is slick!

Also serves as a great example of how JS is standardizing useful patterns that were previously accomplished via bending the language a bit.

Re:How JavaScript Works: deep dive into call, apply, and bind by guntars on Friday July 09, 2021 @06:53PM (#27789439)
You should forget about that too. It's applying a function from one object to something else that it wasn't designed for. It happens to work in this case because $nodeList and Arrays are both array-like, but that's a lucky happenstance.
Re:How JavaScript Works: deep dive into call, apply, and bind by sgentle on Saturday July 10, 2021 @03:18AM (#27791713)
Array prototype methods are designed to be applied to other objects. There's explicit language about it dating back to the first edition of the ECMAScript spec: "The map function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method."
Re:How JavaScript Works: deep dive into call, apply, and bind by guntars on Sunday July 11, 2021 @10:21PM (#27806263)
Yep, I just looked it up and it does indeed say that, at least for the 2016 version when all of these features were introduced. Fine! Carry on applying your maps to your non-Arrays!

Personally I think Array.of($nodeList).map(…) should be guaranteed to allocate once and then there’d be no more discussion here.

https://262.ecma-international.org/7.0/#sec-array.prototype....

Re:How JavaScript Works: deep dive into call, apply, and bind by bluepnume on Friday July 09, 2021 @07:39PM (#27789696)
Right. If NodeList is compatible with Array methods, it honestly feels like it should just inherit those methods. Or implement them separately.
Re:How JavaScript Works: deep dive into call, apply, and bind by megous on Saturday July 10, 2021 @08:10AM (#27792827)
You can just copy them to NodeList prototype.
Re:How JavaScript Works: deep dive into call, apply, and bind by nicoburns on Friday July 09, 2021 @06:31PM (#27789252)
You could do it like this:

    const mapNodeList = (...nodeList) => nodeList.map(myFn);
    mapNodeList(...$nodeList)
Re:How JavaScript Works: deep dive into call, apply, and bind by kevincox on Friday July 09, 2021 @07:06PM (#27789524)
Or simply [...nodeList].map()
Re:How JavaScript Works: deep dive into call, apply, and bind by moron4hire on Friday July 09, 2021 @08:13PM (#27789883)
I don't think you can use splats with a NodeList. At least, the TypeScript compiler complains about it.
Re:How JavaScript Works: deep dive into call, apply, and bind by webstrand on Friday July 09, 2021 @10:53PM (#27790748)
`[ ...document.body.childNodes ]` is legal. You need to have the `DOM.Iterable` enabled in your compilerOptions.libs` for TypeScript to permit it, though.
Re:How JavaScript Works: deep dive into call, apply, and bind by edflsafoiewq on Friday July 09, 2021 @09:37PM (#27790374)
Works for me. Splats work for any iterable.
Re:How JavaScript Works: deep dive into call, apply, and bind by hajile on Friday July 09, 2021 @06:36PM (#27789287)
That creates an entire extra list of garbage.
Re:How JavaScript Works: deep dive into call, apply, and bind by lhorie on Friday July 09, 2021 @07:28PM (#27789635)

    const mapNodeList = (...nodeList) => nodeList.map(myFn);
    mapNodeList(...$nodeList)
Garbage collection really isn't the biggest of concerns here considering this code is susceptible to a stack overflow...
Re:How JavaScript Works: deep dive into call, apply, and bind by chrisseaton on Friday July 09, 2021 @06:42PM (#27789337)
Doesn’t the compiler optimise it away?
Re:How JavaScript Works: deep dive into call, apply, and bind by hajile on Friday July 09, 2021 @06:57PM (#27789470)
Those aren't the same types. One is a DOM array and the other is a JS array. They don't share the same constructor or the same prototype chain.
Re:How JavaScript Works: deep dive into call, apply, and bind by Arnavion on Friday July 09, 2021 @06:47PM (#27789380)
If the goal is to call functions on things whose prototype does not define them, then yes arrow functions cannot replace `call` and `apply`. They can't replace `bind` in such a situation either, but the precondition of this subthread was where `bind` can be replaced with an arrow function, ie the bound function is being called on the same `this` in the first place.
Re:How JavaScript Works: deep dive into call, apply, and bind by Jcampuzano2 on Friday July 09, 2021 @06:31PM (#27789254)
You can do it without either of those with this:

Array.from(nodeList$).map(myFunc)

or if you want to spread

[...nodeList$].map(myFunc)

Re:How JavaScript Works: deep dive into call, apply, and bind by hajile on Friday July 09, 2021 @06:37PM (#27789303)
That creates a new array that must be GC'd.
Re:How JavaScript Works: deep dive into call, apply, and bind by afavour on Friday July 09, 2021 @07:01PM (#27789489)
Is there actually a performance hit, though? Smells like premature optimisation to be worrying about this over readable code until you’ve profiled the actual effects.
Re:How JavaScript Works: deep dive into call, apply, and bind by hajile on Friday July 09, 2021 @07:27PM (#27789633)
yes, there is a performance hit.

In this case, the code size is about the same if you run as-is (if you're doing DOM manipulation, performance is likely a real concern). I'd agree it would be premature optimization if we were talking about moving from a map to a full-blown loop or something, but it's hardly a huge change.

Another great example is chaining myarr.filter().filter().map().map() or some such that I sometimes see. Lodash shortcuts normal map with the assumption of arrays without holes and then uses their own iterators to avoid multiple copies. This results in a huge performance boost.

A final consideration here is transpiling. The above methods would actually increase the code size quite a bit trying to work around any edge cases and hit performance even more.

"Premature Optimization" is always a matter of opinion. Some people might consider implementing quicksort instead of bubble sort to be a premature optimization. Others might disagree with me and believe that you should always use loops instead of forEach or map. In any case, this is hardly the only case for using call/apply.

Re:How JavaScript Works: deep dive into call, apply, and bind by afavour on Friday July 09, 2021 @07:50PM (#27789761)
Obviously there is a performance hit, my point is whether that hit is noteworthy at all. JavaScript VMs are incredibly efficient and have all manner of optimisations that aren’t apparent at first sight. Absent profiling data we can’t know and any best practises are really just shooting in the dark. For example: is Array.prototype.map just as efficient on non-arrays? What does it do internally, do we know it doesn’t create an array anyway?
Re:How JavaScript Works: deep dive into call, apply, and bind by Jcampuzano2 on Friday July 09, 2021 @06:47PM (#27789385)
Sure if you want to get nitty gritty, but you just asked how to replace it in the context of someone saying you can go without call and apply, which both of these do with the same input and output.

Unless your nodelist is insanely large, this is going to be a trivial GC to perform.

Re:How JavaScript Works: deep dive into call, apply, and bind by IggleSniggle on Friday July 09, 2021 @09:14PM (#27790259)
You know, people say stuff like this a lot, and I used to believe it.

But the web just feels like it’s getting slower, grinding up CPU. I think in aggregate, this stuff does matter and it’s just wishful thinking to say it doesn’t.

It’s like at some point as a profession we took the mantra of “don’t prematurely optimize” as an excuse.

Honestly, js feels like a much better language today, especially with ts, and hopefully there are improvements that can be made with modern approaches at the JIT layer, but until then, I’d love it if we could just have a more performant web. It might not matter to you on your powerful dev machine, but it does to me now that I’ve given up the powerful dev machine.

Re:How JavaScript Works: deep dive into call, apply, and bind by gassiss on Friday July 09, 2021 @09:42PM (#27790402)
any loop in a few hundred elements is going be trivial regardless of the amount of work you do.

I don't think the web is getting slower because we're using more map() instead of for...of loops. It's getting slower because what is being developed is getting more complex.

Feature bloat and excessive network load are the main reasons that the web feels slower imo.

Re:How JavaScript Works: deep dive into call, apply, and bind by IggleSniggle on Friday July 09, 2021 @10:24PM (#27790634)
I’m not sure this is a true disagreement is what I’m saying.

I can tell you that when I navigate around the web doing nothing more than having some web apps open, the UI for the whole OS will start stuttering and the fans will kick into action. `top` says this is all chrome.

I buy that the problem is “feature bloat,” but I also think a great deal of these features are possible with a heck of a lot less code and a heck of a lot less pointless GC etc.

.filter().map() may not itself be alarming for a few hundred elements…but it is for a few hundred elements multiplied by a few hundred features. If you still want those few hundred features, maybe it would be nice if some thought had gone into making them individually perform better.

Tangent: I love the Unix philosophy! Do one thing well. Nothing more satisfying than being able to pipe inputs to outputs and put together little programs out of modular components. But when you want to build a program, you don’t do that, because you care about the “well” part of “do one thing well.”

Context does matter for a lot of this, and I think there’s probably a lot of grey area here. Doing something high level? Whatever, just get the job done, focus on readability, don’t worry about the weird performance edges of it. It’s just that those chunks have a tendency to become bigger chunks and get included where they were never intended, or where their underlying implementation is accidentally replicated multiple times for slightly different outputs (in the abstract, like doing .map().map().map()).

Anyway I can tell I’m losing the thread and ranting mindlessly now…

Re:How JavaScript Works: deep dive into call, apply, and bind by bluepnume on Friday July 09, 2021 @07:27PM (#27789630)
The fact that NodeList doesn't have a native map feels to me like a wart, and doesn't really justify `Array.prototype.map.call` having to exist as a fallback in the language.
Re:How JavaScript Works: deep dive into call, apply, and bind by greggman3 on Saturday July 10, 2021 @04:46AM (#27792070)
no they can not.

arrow binds a different "this" than call and apply. Call and apply get the this passed in at call time. Arrow gets the this at creation time.

Re:How JavaScript Works: deep dive into call, apply, and bind by Arnavion on Saturday July 10, 2021 @05:21AM (#27792210)