Pain in the arse int, parseInt()
How often do you use parseInt()? Often enough to have run into the unexpected "starts with 0" problem? Have you ever tried: parseInt("08"), only to get a return of 0? Let's find out why.
To start, parseInt() accepts a base as the second argument. You may sometimes see plugins use parseInt(number, 10), wonder why added this mysterious 10 as a second argument. Or, perhaps you knew about the base parameter, but wonder why the explicitly specify it.
The parseInt("08") issue identified above is the reason. Computers love base 2, but base 8 (octal) and base 16 (hexadecimal) are also happily received as they are powers of 2. We use the hex base commonly for RGB color and other values, so languages developed a syntax to easily specify the numeric base. In JavaScript, this notation is as follows:
0xnumber = Hexadecimal
0number = Octal
You can actually use this is a numeric literal:
x = 0xF;
15
x = 033;
27
The octal notation comes as a surprise, especially if you've even been unlucky enough to accidentally precede a numeric literal with a zero. Why JavaScript couldn't use a notation like "0w33", I will never know.
Everything I've said to this point typically does not present an issue in everyday JavaScript. However, when you introduce user input and other string values, our old friend parseInt() comes to the rescue, but not without terms. A user will never type 0xFF in place of the base 10 number 255. But, if a user is asked for their birthday, and they use "08" to represent the month of August, we now have an octal number! What's worse, an octal number that is invalid (there's no 8 in the octal base).
parseInt("08")
0
And, that concludes the very long winded rationale for always adding the number base parameter 10, when you are converting strings to integers.
parseInt("08", 10)
Time out, I see something eval() happening here
I'll be brief with this one, as I assume most people know about this. However, I still see quite a bit of JavaScript floating around that suggests using "quoted" code within the setTimeout() function:
setTimeout('do = "this"', 3000);
I can't say for sure, but everything I know about JavaScript tells me that setTimeout() will be performing an eval() on the quoted code, which we know is inefficient.
setTimeout() can, and should be given a function as it's first argument:
setTimeout(function() { do = 'this'; }, 3000);
Can't touch this
The this keyword provides a reference to the current object, it provides context. But, we lose that context when we make a call to another function. How often do you find yourself doing this:
(function() {
var _this = this;
doSomething() {
console.log(this); // this is no longer meaningful - it doesn't refer to the original object
_this.value = 'I had to copy this to a local variable so I still have a reference to it';
}
doSomething();})();
In this case, once we stepped inside the inner function, doSomething(), we lost all context that this provided. But, we were able to maintain that context, by storing it in a local variable called _this.
I don't know if I'd call this a bad practice, I see it a lot, it works, but perhaps doesn't do a good job of explaining what _this is. Perhaps the variable should be named something more appropriate, like:
var myParent = this; //or
var myCaller = this;
But, there's a way we can avoid this temporary reference to the this context. We can do this by using the built-in call() method.
(function() {
doSomething() {
console.log(this); // this is now usable, it references the calling object
this.value = 'I have no need for a temporary reference to my caller's this context';
}
doSomething.call(this);
})();
call() allows us to "pass in" the context that the function should have. So, we're essentially saying, "when I call you, stay in the same context, you don't have a context, suck it!"
jQuery tells us this is okay by providing a context parameter to their ajax method. This parameter provides the callback methods with the proper context. Usually, we just pass in this.
Semicolons are super important
Okay, so nobody likes semicolons. The word semicolon doesn't even really make sense, how can something that's "semi" be larger than the whole thing (colon)? But, as a symbol, they make a lot of sense to the JavaScript interpreter. They say, "Hey, this statement has reached it's end."
The problem is, the interpreter is not strict on their usage, most of the time you can get away without using a semicolon at the end of a statement. This flexibility actually leads us to believe that they're optional, and, in most cases, they are.
But, what happens when you create an object, forget to close it with a semicolon, then try to run an anonymous function? Surprise! We get an error. Try the following code to see for yourself:
x = { x: 'y'}
(function() {
var y = x;
})();
Uncaught TypeError: object is not a function
Drop a semicolon at the end of the first statement, and you can continue on your way.
Keep your window clean
If I were to describe JavaScript to a C++ or Java developer in one sentence, I'd say, "It's powerful, flexible and fun, but it's a lot like learning the English language." The English language has some quirks, such as:
Plural of goose = geese
Now, you're not going to cause any damage if you say, "I fed the gooses this morning", but you can really make a mess when you misuse the JavaScript window object. The window object is a product of the browser JavaScript engine, it provides us with methods and properties that allow us to interact directly with the web browser. But, it often creates a confusion for the following reasons:
- It lacks cohesion among it's properties and methods:
- window.length() returns the number of frames that the window contains. Huh? The window object contains a frames object. Why would window.length() be assigned the number of elements in that frames object.
- window.open() creates a new window. It's confusing to use one instance of an object to create another instance of the same object.
- what happens when we decide provide access to other browser tabs that have loaded the same domain? window.tabs() doesn't really make much sense, yet window is the "top-most" object.
- window.length() returns the number of frames that the window contains. Huh? The window object contains a frames object. Why would window.length() be assigned the number of elements in that frames object.
- That lack of cohesion is made worse once you add "global" objects in your own code
- When you define a variable outside of a local scope, it becomes a global variable, accessible by any and all other objects, across all scripts loaded in that page request.
- The window object becomes the new home for these global variables:
a = 1;
window.a
1
a === window.a
true - You're a smart programmer, so you use the local scope provided by the var keyword:
function doThis() {
var b = 1;
}
window.b
undefinedbut,
window.doThis === doThis
trueSo, the window object is a little greedy, it not only wants your global variables, but your global functions. It seems like a catch-22. How can I keep the global space clean by using, then disposing a local variable, if doing so requires that I create a global function?
The JavaScript dudes thought about this though, and decided to allow a function to be created and called without ever assigning it a name and position in the global space (window object).
They call this the anonymous function:
(function() {
var a = 1;
})();
window.a;
undefinedEssentially, what we did was create a function without a name, but wrapped the entire function definition in parenthesis. We did this, so we can call it with the ( ) function-call syntax we're familiar with, directly after we define it.
So, we not only kept a variable from being added to the global space, but also prevent a function name from taking a seat at the global table.
- When you define a variable outside of a local scope, it becomes a global variable, accessible by any and all other objects, across all scripts loaded in that page request.