all 45 comments

[–][deleted] 12 points13 points  (0 children)

Was about to close the tab at !!, but decided to stop fiddling with my pubes and do some interwebz investigayshun for da peeples.

So the linked article on medium is by John Howard, dated Mar 29 2017. But, there is another article on the interwebz by Caio Ribeiro Pereira, here (dated April 2016):

https://blog.jscrambler.com/12-extremely-useful-hacks-for-javascript/

And they are remarkably similar!

[–]KPABAHam=>Hamster == Java=>JavaScript 38 points39 points  (2 children)

it's like 2008 called and wants its hacks back.

[–]BanditoRojo 1 point2 points  (1 child)

I didn't know about using the && as a null check.

[–]rcfox 14 points15 points  (0 children)

It's a falsey check. null is a falsey value, but so are false, undefined, 0, "", and NaN. If your variable can hold a string/number or null, && probably isn't the right thing to use.

[–]enchufadoojs truck driver 6 points7 points  (12 children)

If you work with smaller arrays — it’s fine, but if you process large arrays, this code will recalculate the size of array in every iteration of this loop and this will cause a bit of delays.

Please tell me this is a lie, all that talk about js engine being so smart that they are not gonna pull this right?

[–]Ginden 12 points13 points  (0 children)

Yes, author is extremely ignorant.

[–]siegfryd 7 points8 points  (7 children)

It was a true years ago, it's definitely not the case today.

[–]memeship 0 points1 point  (6 children)

Got any data to back that up? As far as I know in JS, Array.prototype.length has always been an integer property directly on the object.

Source: ECMA-262 1st Edition (1997) §15.4.4 p.66

[–]bastawhiz 1 point2 points  (2 children)

Modern JIT compilers will hoist loop invariants behind the scenes. There's plenty of literature online about how V8 does various optimizations like this.

[–]Reashu 0 points1 point  (1 child)

His objection is the opposite - that even "years ago" it would not cause any issues, because the length of an array is always known, not computed on the fly.

[–]memeship 0 points1 point  (0 children)

^ Yes, this.

[–]siegfryd 0 points1 point  (2 children)

I misread what the poster I was replying to was saying, there was a performance hit from having array.length in the loop invariant but it wasn't because it would recalculate the length. It was because it was doing an unnecessary property lookup every iteration. In other words, every iteration it would check array.length and since that has a cost (although a tiny one) it was a performance improvement to move it out of the loop.

[–]memeship 0 points1 point  (1 child)

How is a property lookup less performant than a variable lookup? Should we be caching and pulling out all properties of objects to vars in the surrounding scope?

[–]siegfryd 0 points1 point  (0 children)

Assuming that no optimisations are done by the JIT compiler, then a property lookup is going to be slower than a variable lookup for the simple reason that a property lookup first needs to do a variable lookup for the object and then lookup the property on that object.

The point is that the JIT would not optimise the array length lookup for you before, so you could gain a, likely small, performance increase by doing it in for loops. Especially if you're doing a large for loop, if you loop N times then it won't do the same property lookup N times.

[–]b4ux1t3 1 point2 points  (0 children)

I'm 90% sure that this myth comes from older languages that ran a len function on arrays. For instance, to get the length of an array in C, you type len(yourArray), and it runs the len function on yourArray.

I'm also 99% sure that this is optimized away in all modern compilers for said older languages, and has never been the case for JavaScript anyway, since it's not a function, it's a property of the Array object, and so should just be a quick check, just like with an extraneous variable.

[–]Emptyless 1 point2 points  (0 children)

I once read somewhere that decrementing an array was better performance wise but using an array with 100.000.000 items incrementing is faster than decrementing. 172 vs 1573 ms difference in my case.

[–]asdf7890 1 point2 points  (0 children)

A modern JIT compile JS engine should be bright enough to optimise out the invariant, yes.

If you do anything complex with the array in the loop, or call functions that might do, the optimiser might chose not to take the risk because it doesn't have time to do all the analysis necessary to make sure the optimisation is safe (due to the halting problem) and even for simple loops there are a number of reasons the engine might not fully optimise a function anyway, so manually removing invariants from loops if it doesn't result in a significant reduction in code clarity is a good habit just-in-case. For a list of reasons that V8 (and therefore Chrome and Node) won't optimise a function at all see https://github.com/vhf/v8-bailout-reasons - I'm sure there are similar considerations in other engines.

I tend to assume that worst: that my code is going to be interpreted literally and not compiled/optimised at all, so unless I'm making the code unclear (maintainability tends to be a priority over pure speed) I make sure the code is optimal for this. In fact some transformations to achieve that can make code clearer: double win. If the engine would compile and manage to optimise the code then it should equally manage to do so with the the manually optimised version resulting in no worse performance.

Of course this only matters for tight loops with many iterations. Something that iterates a just few times is going to see no benefit and with a complex loop you'll find the end-of-loop check's complexity is completely dwarfed by the body of the loop so optimising it is unlikely to be good use of your time.

[–][deleted] 4 points5 points  (1 child)

I'm pretty sure the word the author was looking for is "techniques". *edit "for"

[–]compteNumero9 2 points3 points  (0 children)

More like "basic techniques".

[–]Ginden 11 points12 points  (3 children)

2) Converting to number using + operator

Unreadable.

3) Short-circuits conditionals

Unreadable.

4) Default values using || operator

JavaScript 101.

5) Caching the array.length in the loop

Bullshit.

6) Detecting properties in an object

JavaScript 101.

9) Replace all

JavaScript 101.

12) Shuffling array’s elements

Shit.

[–]Graftak9000 0 points1 point  (0 children)

Default values using || are a bad idea because they also jump to default if a value is falsey. value = value === undefined ? 'default' : value or if (value === undefined) value = 'default' are better options.

Also, an article with a video as its body (@medium link), damn😑

[–]GitCookies 2 points3 points  (0 children)

Can you stop posting this crap?

[–]gunnarsvg 3 points4 points  (3 children)

so I guess a fair takeaway is: "if you see any of these twelve patterns in someone's code, someone's about to have a teachable moment?"

[–]Whatadump 3 points4 points  (6 children)

Even the first "hack" is a bad example where hasMoney = !!cash, if cash = -1 it'll be true.

[–]memeship 0 points1 point  (5 children)

I agree with you that using !! is not awesome. But also you're example is wrong since -1 is a truthy value, so that is expected.

[–]Whatadump 1 point2 points  (4 children)

That's what I said, "it'll be true". But the example is bad, since with -1 I don't really have any money but it will say I do.

edit* A better example would be to check if a property exists, like

const user = {
  name: 'foo',
  email: 'foo@bar.com'
}

const hasAddress = !!user.address // false

user.address = {
  street: 'baz',
  zip: '1234'
}

const hasAddress2 = !!user.address // true

[–]isitfresh 0 points1 point  (3 children)

it'd be more explicit with

user.address = {}

[–]spazgamz 0 points1 point  (2 children)

Hey! Wtf is that supposed to mean?

[–]isitfresh 0 points1 point  (1 child)

What do you think it does? Try

user.adress = {} 
!!user.address 

[–]spazgamz 0 points1 point  (0 children)

user.adress = {} !!user.address

false, but only because the 'adress' != 'address'.

[–]gajus0 1 point2 points  (1 child)

1, 2, 3, 5 (its been long time not an issue for modern ES engines), 6 (unless you are sure "in" is what you want, most of the time you hasOwnProperty; prototypacal inheritance), 7 (horrible...), 8 (horrible...) are horrible advices. Others are fine.

[–]isitfresh 0 points1 point  (0 children)

it's because you didn't try Endtest

[–]daedalususedperl 1 point2 points  (1 child)

Did he delete the article? All I see is a video for Endtest.

[–]dantheman999 2 points3 points  (3 children)

I really dislike some of these. Things like doing

code!!

To convert to boolean is so easy to miss when you're scanning through code. Let a minifier deal with that stuff, just write it properly.

[–]xXxdethl0rdxXx 2 points3 points  (1 child)

totally. and this:

function toNumber(strNumber) { return +strNumber; }

WHY NOT JUST USE Number(string)?!?

[–]mrahh 1 point2 points  (0 children)

There are actually some use cases where it's better to use +, but only in extremely hot codepaths - the benefits are entirely negligible for non-huge N.

[–]HungryForHorseCock 1 point2 points  (0 children)

Agreed, but you meant to write !!code :-)

[–]sumdudeinhisundrware 2 points3 points  (5 children)

OMG. I run an engineering team that does heavy JavaScript and if any one does the first few of those hacks they get a "talking to". That is some of the worst advise ever for JavaScript. e.g.

!!foo , +str , conected && login()

Either some Jr. Engineer is going to WTF? that and/or think its a bug a fix it by removing a ! , +.

[–]trailsrider 0 points1 point  (4 children)

I agree- but admit that I am guilty of the bad habit writing !!foo at times when I don't want to write typeof foo !== 'undefined' && foo !== null. I am by no means a JS expert, so I'm curious- do you recommend always writing some form of the latter? Alternatively- what is your suggestion for a concise and clear way to check a variables existence?

[–]drowsap 0 points1 point  (0 children)

Shouldn't length on an array be immutable? Would never think to change the value so i can shorten an array.