Many times I read or write silly stuff and often it takes a while before I realize "
what dafuq did I just read ?"
Well, this post is about just few
a-ha moments, together with
WTFs, I keep having from time to time: enjoy!
Those 4 Repeated + Disturbing Bytes
I mean, this must come first, right? Web Development Tools are improving like hell and Web App development are 99% of the time behind a build process able to shrink HTML, recompile and optimize CSS, eliminate even death code from JS after parsing, recompilation, minification, pre-gzip-compression ... you name it ... and finally, we gonna have the output with a file called:
mystuff.min.js
?!?!
Am I the only one thinking that this does not make sense and all files should be named by default
.max
so that the equivalent after minifcation won't include those damn 4 bytes per each file?
Entire CDN dedicated to common libraries and these expose minified files with
.min
extension ... isn't this bloody brilliant?
The "Not So Smart" Condition
This time is about logic, and the fact that order matters. "
Which order?" you say? Thanks for asking!
// knowing only one stuff item has a value
for (var fValue, i = 0; i < stuff.length; i++) {
fValue = parseFloat(computate(stuff[i])) ||
fValue;
}
Above code might look weird but it's actually more common than we can imagine.
Think about defining, as example, a prefixed method so that only one of them will be available. As soon as the value is not undefined we keep going checking for no reason something we don't need so ... can you spot the difference?
// knowing only one stuff item has a value
for (var fValue, i = 0; i < stuff.length; i++) {
fValue || (
fValue = parseFloat(computate(stuff[i]))
);
}
If
fValue
has a value there is no reason to perform other expensive function calls. Moreover, how can we be that lazy that breaking a for loop is one of those things I basically never see in production code ?
// knowing only one stuff item has a value
for (var fValue, i = 0; i < stuff.length; i++) {
if (fValue = parseFloat(
computate(stuff[i]))
) break;
}
Scary ... uh ? Point here is: always think about the execution order, logic, cost of your boolean expressions ... you might realize swapping few things around could result in better performance easily while adding a break, sometimes, will be smarter than anything else.
Not Just Missing The Falsy
This is the most common one, no way JS developers will learn this differently. I have
tried many times but there's nothing to do, bad habits are bad habits!
Let's see what I am talking about ...
obj.prop = obj.prop || defProp;
Can anyone answer exactly what are we trying to do up there? ^_^
Don't bother, here the summary and ... NO, we are not trying to do what we think!
- access a possibly undefined property to a generic object
- if that was a getter, invoking a getter of that object
- either getter or property, verifying after lookup that value is not
""
, 0
, false
, NaN
, null
, or undefined
- even if defined, discarding any of previous valid values in order to lookup for the variable
defProp
, assuming this won't have similar values we already discarded or the sequence could be infinitely pointless if repeated - once a value has been addressed, if any, assign as the property of the object
- if this has a setter, invoke the setter for no reason potentially reacting for no reason as property update.
- if the prop had a getter only, we have simply raised an Error ^_^ and without any valid reason
Nobody Uses Getters/Setters Anyhow
Excuse me? It's
even worst than we think since one of the most powerful, cool, useful, tools ever for JS has been
approved in harmony:
Object.observe().
Can you imagine a
defProp
as
NaN
by mistake so that these three lines will most likely cause three records notifications because we are using a dumb pattern to assign a property if not defined yet? ^_^
// NaN != NaN => true
var defProp = NaN;
obj.prop = obj.prop || defProp;
obj.prop = obj.prop || defProp;
obj.prop = obj.prop || defProp;
// 3 notification records
// 1 as "new", the one we are interested
// 2 as update, with "same value" ...
Just Brilliant, and
moArover this habit is so common that whoever will decide to simulate
Object.observe()
through
Proxy
(I am already, stay tuned) will need to guard the Records creations and notifications avoiding redundant notification because developers keep reassigning for no reason!
As Easy As This
// the in operator does not trigger anything
"prop" in obj || (
obj.pro = defProp
);
// alternatively, no trigging
obj.hasOwnProperty("prop") || (
obj.prop = defProp
);
There, you better embrace this pattern or you gonna have bad time soon either with Proxy, already available, and Object.observe, specially when we don't know who created the object and how. So, once again, repeat with me:
avoid property access and/or reassignment for no reason, no excuse!
Bitwise To The Rescue
Rather than understand them, we often avoid them ... well, we shouldn't. Bitwise are cool and let us make a lot of things.
These days we are discussing
Collection#has(key)
and
Collection#contains(value)
where latter one could be the equivalent, if it won't use
identicalType()
to
-1 < arr.indexOf(value)
or, as I can read in many source code, the boring
arr.indexOf(value) !== -1
.
How about
if (~arr.indexOf(value)) { ... }
to know if an Array contains a value? Problems only with massive collection length but that's 99% of the time not our case.
If you are wondering what's going on there, consider that
~
is the equivalent operation of
-(x + 1)
where
-1
is the only number that becomes
0
, the falsy value, while all other indexes, included
0
, will be
-1
or lower number, all truthy.
Smart Configuration
How many times we end up defining configuration options as strings ? ie,
"shrink",
"minify",
"lint" ...
And how many check to define one or more behavior, accordingly to these strings?
And how about having same behavior within different configuration options ?
if (obj.option == "shrink" || obj.option == "minify") { ... }
or even this:
/^shrink|minify$/.test(obj.option)
, creating a RegExp object maybe for each time we want to check that option ... how about this:
var Option = {};
Option.SHRINK = 1 << 0; // pow(2, 0)
Option.MINIFY = 1 << 1; // pow(2, 1)
Option.LINT = 1 << 2; // pow(2, 2)
// etc ...
Option.ALL = Option.SHRINK |
Option.MINIFY |
Option.LINT;
Option.NUT = Option.ALL &
~Option.LINT;
// test
alert([
Option.SHRINK & Option.MINIFY, // 0
Option.LINT & Option.MINIFY, // 0
Option.MINIFY & Option.MINIFY, // 2
Option.SHRINK & Option.ALL, // 1
Option.LINT & Option.ALL, // 4
Option.MINIFY & Option.ALL, // 2
Option.ALL & Option.ALL, // 7
Option.SHRINK & Option.NUT, // 1
Option.MINIFY & Option.NUT, // 2
Option.LINT & Option.NUT, // 0
Option.ALL & Option.NUT // 3
].join("\n"));
The easiest way to configure an object is via
obj.option = Option.LINT | Option.MINIFY
, as example, and we are sure that all associated operations will be performed ... how ? As example, with a switch :)
switch (!!obj.option) {
case !!(obj.option & Option.ALL):
// perform linting first
case !!(obj.option & Option.NUT):
// perform minification shrinking ?
minify(
obj.option & Option.SHRINK
);
}
If we don't like the double
not
we can always use a
function b(v){return !!v}
and make it look gorgeous, isn't it?
Enough for this time
These are just few things I've been dealing with recently either with my projects or in some mailing list. More will come as soon as I spot it, of course ... right now, just think about these patterns and hint, maybe your code could make more sense, becoming more readable, easier to maintain, future proof, and all this with very little extra effort. Have a nice week end.