Every now and then some people will come out and say, that
you shouldn't use the keyword undefined in your JavaScript code.
Because undefined isn't actually a real keyword, just a global
variable. So while the following code might seem to work:
var message;
if (message === undefined) {
message = "Hello";
}
alert(message); // prints Hello
It can easily fail when somebody happens to assign some value to
undefined:
var undefined = "blah";
...
var message;
if (message === undefined) {
message = "Hello";
}
alert(message); // prints undefined
Therefore it's safer to use the typeof operator to check for
undefined:
var undefined = "blah";
...
var message;
if (typeof message === "undefined") {
message = "Hello";
}
alert(message); // prints Hello
Or something like that the argument goes... but I don't buy it.
The often-suggested solution to use typeof operator isn't really the
same as comparing to undefined:
var foo;
alert(foo === undefined); // true
alert(typeof foo === "undefined"); // true
alert(bar === undefined); // ERROR!
alert(typeof bar === "undefined"); // true
The typeof operator will return "undefined" also when the variable
itself is not even declared. This can be useful in some situations,
but most of the time you would instead like to know when you are
mistakenly trying to access an undeclared variable - using typeof
will hide those errors from you.
Sometimes you really are better off without using undefined. The
code example at the beginning of this post would be better written
without no comparison at all:
var message;
if (message) {
message = "Hello";
}
alert(message); // prints Hello
But sometimes it's really-really hard to not use undefined:
var person = {
name: undefined,
age: undefined
};
How would you construct this object without using undefined? Here
the person object contains two properties that just don't happen to
have a value. Not to be confused with empty object, which doesn't
even have the keys:
var person2 = {};
alert(name in person); // prints true
alert(name in person2); // prints false
One way to achieve this is to exploit the fact that empty function always returns undefined:
var person = {
name: (function(){})(),
age: (function(){})()
};
That's clearly undefined-free, but it's also the kind of code that might make you wanna run away screaming.
A better approach is to define your own local undefined variable:
(function(){
var undefined;
var person = {
name: undefined,
age: undefined
};
})();
Here the local undefined shadows whatever the value of undefined
happens to be outside the closure. That's just as readable as using
global undefined, plus it has a nice side-effect, that when you run
your code through a minimizer, the lenghty undefined will be
replaced by something like a, saving you precious bytes:
(function(){var a;var person={name:a,age:a}})()
But in reality there is no reason for anyone to change the value of
undefined. And when somebody mistakenly changes it, then it should
be OK that their program comes crashing down as loudly as possible, so
they can go and fix their mistake.
JavaScript makes it possible to redeclare many global variables - just
because it is possible for somebody to declare alert = "foo" doesn't
mean that you shouldn't use alert in your code. It's the same with
undefined.
Many well-known JavaScript libraries (e.g. Prototype, Dojo)
make the assumption that people don't mess with undefined - so
should you.
Kirjutatud 27. jaanuaril 2010. Avalda oma arvamust.
To strive for shortest load times you want to make all your CSS and JavaScript files and images to be highly cacheable. So you send all kinds of headers that tell the browser to cache these files forever and never ask them from server again. Great. Now your website is comparable to the speed of light. Your server almost has no load, your clients are happy... and then you need to make a change in one of those cached files.
Because the file is cached, it's not enough to just put up a new version of it. We explicitly ordered browsers to never ask for new versions of files. Sure, we could ask our clients to clear their browser cache and reload the page, but that's far from good user experience and besides - it doesn't neccessarily work either when the file happens to be cached by some intermediate proxy server. So the only really reliable way is to rename the file.
So you rename your my-app.js to my-app2.js and then to
my-app3.js and so on... but that's getting tedious. Especially
when you have a lot of files to keep track of and you want to deploy
new versions of you website as often as possible. So you need to come
up with some automated scheme for renaming your files in each build.
Here are some choices:
styles-3.0.1.css
main-3.0.1.js
slider-3.0.1.js
rich-editor-3.0.1.js
A simple and often implemented scheme. But with several drawbacks:
styles-2483.css
main-2483.js
slider-2483.js
rich-editor-2483.js
That's a lot more automated and we can also use it for internal test-deploys. But the third problem still remains. Additionally this technique doesn't work when you either don't have a centralized build system or you don't have a version control system available to give you the revision number when you make the build.
styles-ae73776736945d07512d810f10a0c47a.css
main-0a72aa2e0d6e426cddfb3ff0e25ed7b8.js
slider-fe37cbafab5a2546a9ba0062a2009b4c.js
rich-editor-5fdc749b7075c23b36cdbe4fb3ec1be8.js
Here each filename contains an MD5 hash of its contents. This ensures that filename changes only when it's content changes. Although we recalculate the hashes in each build, the files who's content didn't change will get the same hash they had before.
The only disadvantage I see is that it adds quite a bit to the length of filenames. But this seems easy to overcome. First of all it's not really the hash that's so long - it's only the hexadecimal representation of the 128-bit hash, that's so long. One could use base64-encoding to reduce the hash a bit:
styles-2kStTdDfHT1r4jWGyGeCHw.css
You could also only include the first half of the hash:
styles-2kStTdDfHT1.css
This theoretically means entire 1 out of 264 chance of getting a collision - you are more likely to be killed by a meteorite.
Anyway. That's the technique I have chosen to implement for SeeMe. Hopefully it will work just as fine in practice as in my theory.
Kirjutatud 18. jaanuaril 2010. Avalda oma arvamust.
Say you are creating an array of something, for example UI buttons:
var buttons = [
new OpenButton(),
new ExportButton(),
new SaveButton(),
new DeleteButton()
];
Nice and clean code. But then the requirements change...
Marketing department decides, that ExportButton should only be shown
to paying customers, and otherwise removed from the UI completely.
This might be very well be a good business decision, but from the code perspective it's not good at all. When the order of elements in array doesn't matter we could get away with a code like that:
var buttons = [
new OpenButton(),
new SaveButton(),
new DeleteButton()
];
if (isPayingCustomer()) {
buttons.push(new ExportButton());
}
But in our case the order definitely matters - UI buttons can't just be in any random order. And therefore we have to split the creation of array into multiple parts, which results in quite ugly code:
var buttons = [new OpenButton()];
if (isPayingCustomer()) {
buttons.push(new ExportButton());
}
buttons.push(new SaveButton());
buttons.push(new DeleteButton());
We went from nice and clear declarative code into much harder to understand imperative code. That's a great price to pay for such a small change in functionality. Could we do it better?
Instead of conditionally pushing things to array, we would like to just declare that this array element only exists under certain conditions. We could express it like that:
var buttons = [
new OpenButton(),
isPayingCustomer() ? new ExportButton() : null,
new SaveButton(),
new DeleteButton()
];
That's all nice and clean, except that we didn't want any null
elements in our array. The code that will use this array probably
expects all array elements to be buttons, and we don't want to change
it to ignore nulls.
Luckily there is compact() - a method of arrays that removes all
null and undefined elements from array. That's exactly what we
need! We just add a call to it at the end of our code:
var buttons = [
new OpenButton(),
isPayingCustomer() ? new ExportButton() : null,
new SaveButton(),
new DeleteButton()
].compact();
The only catch is that JavaScript arrays don't actually have a
compact() method. But that's not a big problem - anyone can add it
to Array.prototype and it's already there in some JavaScript
libraries (e.g. Prototype). Here's a quick implementation using
filter():
Array.prototype.compact = function() {
return this.filter(function(x) {
return x !== null && x !== undefined;
});
};
compact() does something so simple that it rarely gets attention.
But that simple tool has often saved me from writing some truly ugly
push-push-push-code. I hope it helps you too.
Now go and compact those arrays!
Kirjutatud 13. jaanuaril 2010. Avalda oma arvamust.
RSS, RSS kommentaarid, XHTML, CSS, AA