In the spirit of
- Common programming mistakes for .NET developers to avoid?,
- Common programming mistakes for PHP developers to avoid?
what are common mistakes we should avoid for JavaScript?
In the spirit of
what are common mistakes we should avoid for JavaScript?
Trying to use objects as property names / associative array keys. For several purposes it will look as if it worked, but it didn't; the object was coerced to a string representation.
Rookie mistake #1: Building difficult, browser-compatibility-sensitive operations by hand instead of using jQuery.
accidentally comparing anything (like an int, boolean, etc) to a string without type checking (i.e. the === operator)
Don't use document.write().
Declare variables with 'var' (keeping them from being globals).
Read Javascript: The Good Parts - an amazing (and concise) book by a javascript pioneer that will answer this question perfectly.
Missing var, and because of this magic erros, when i is changed out of the expected scope:
for(i = 0; i < 10; i++){
}
should be:
for(var i = 0; i < 10; i++){
}
To clarify:
var i = 10;
function ii() {
for (i = 0; i < 10; i++) {
test();
alert(i);
}
}
function test() {
i++;
}
ii();
Output:
1, 3, 5...
With
for (var i = 0; i < 10; i++)
Output:
1, 2, 3
Failing silently when JavaScript is disabled on the client.
Ideally we should all be using progressive enhancement so there is no loss of core functionality when JS isn't available.
One mistake I experienced was that programmers are trying to apply e.g. common Java or C++ design patterns to JavaScript that didn't make any sense in that language and therefore overcomplicating JavaScript development.
jQuery or many other libraries/frameworks may have at the beginning a quite uncommon approach (for old school OO typed language developers) to solve problems but it is usually way more effective once you got into it.
An of course not using one of the great libraries is a huge mistake, too.
Using any of escape()
, encodeURI()
, and encodeURIComponent()
when you need one of the other ones. This is really good at introducing incredibly subtle, hard-to-debug errors into Ajax interactions. See this lovely comparison of the three.
!=
/==
instead of !==
/===
to check type as well as equality. "1"==1
is true; "1"===1
is falsevar
keeps it in the local lexical scope. Not putting it there puts in globally.for(var item in myArray)
**: It just happens to work because the functions, like join
are marked as non-iterable. If you extend the array (as many libraries do, your code will break). That construct is meant for iterating over object members. For example:
var obj = {variable1: "hello", variable2: "world",
function1: function() {
alert("func");
}
};
for(var p in obj)
alert(p + ": " +obj[p]);
//p will equal "variable1", "variable2", "function1"
var arr = ["a", "b", "c"];
//using the same construct as above still works, but just wait.
arr.myFunc = function() { alert("uh oh"); }
//now looping in that same way will cause p to be "myFunc" in an iteration
//which you may not have intended
From a non-technical standpoint, one of my own personal biggest mistakes was diving right in and trying to write all my own code to solve my problems.
Turns out, it's next to impossible to encounter a problem that no one else has already solved, so do yourself a favor and spend 5 minutes searching 'snippet' websites to see if there is already a solution.
As far as technical mistakes, since javascript is loosely typed, it's easy to accidentally compare two values like an int to a string or something.
Always taint-check strings before adding them to the DOM, calling eval(), or doing anything that might expose the string.
Putting a comma after the final element of an array/object literal, like any sane language allows you to, and in fact JavaScript does on any browser that doesn't suck.
Yes, IE's fascist comma syntax irritates me a little tiny bit.
Example:
var newObj = {
foo: 1,
bar: 2,
baz: 3, // works on any browser that doesn't suck
};
var newObj = {
foo: 1,
bar: 2,
baz: 3 // works on IE
};
Trying to add the value of two numeric string objects, usually from text input, which results in a concatenation of the strings. You have to parse the numeric value of the strings with parseInt() or parseFloat() first.
Some resources to check out:
var x = "0100";
var y = parseInt(x); // result _could_ be 4, 64, or 256 -- no radix specified
Do this instead:
var x = "0100";
var y = parseInt(x, 10); // result WILL be 100
Simple thing but all too often, whether out of laziness or whatever, many JS coders tend to leave out the semi-colon at the end of a line.
Forgetting that in JavaScript data constructors that the numbers for the months January to December range from 0 to 11 (so that month 1 is February, not January).
The fact that javascript "doesn't require" semicolons at the end of lines can lead to bizarre bugs. Here's an example:
return
{
foo: 1
};
This won't actually return the object because it will parse if the parser adds a semicolon after the return. You really need to use 1TBS in situations like this.
As asked for, this should be
return {
foo : 1
};
1. JavaScript has only function scope unlike other C like languages which have block scope as well as function scope.
function test()
{
var age = 10;
var name = 'yogi';
if(name === 'yogi')
{
var age = 20;
alert(age); //alerts 20.
}
alert(age); // woohoo. alerts 20.
}
2. Be careful with your semi colons and commas in JavaScript.
function test()
{
return //JavaScript will insert an automatic semi colon here.
{
output : true
}
}
alert(test()); //alerts undefined.
3. Suggesting that you won't use a JavaScript library because the project is too small.
4. Trying to change JS so that it fits your existing knowledge. I am looking at you Objective-J.
5. Not understanding how 'this' works in JavaScript.
6. To believe that in JavaScript, everything is an object. There are primitive types in JavaScript.
function test()
{
var person = "yogi";
person.age = 100;
alert(person.age); //alerts undefined.
}
That's all I can think of right now, will add more as I think about it some more.
HardCoding clientIds to manage the DOM if wou're working under asp.net
Running a RegExp
object created with the g
flag multiple times without taking into account the state of the lastIndex property...
Trying to add properties to an object by modifying a property named "prototype", rather than modifying the prototype
property of that object's constructor function...
var obj = { ... };
obj.prototype.blah = function() { ... }; // does not do anything useful
Using global variables.
This is mostly a problem on large projects, when you use someone else's variable names. The solution is to hide all of your code in namespaces or closures. For example:
Running code without overwriting other variables:
(function() {
var a = 2;
var b = 2;
var sum = a + b;
alert("By the way, " + a + " plus " + b + " is still " + sum);
})(); // this creates and then calls the anonymous function
Putting code in a namespace: Basically, just use a lot of dictionaries.
<script type="text/javascript">
if (!window.myprogram) { // make sure the namespace is "declared"
window.myprogram = {};
}
myprogram.myapp = {
variable1: 2, // vars and functions are declared the same way
function1: function(a, b) {
return a / b; // whatever
}
};
</script>
Using arrays as associative arrays...
var a = new Array();
// ugly syntax...
a['key'] = 1;
should be rewritten as
var a = {};
a.key = 1;
Using eval()
to access properties by name...
function naiveGetProp(obj, name)
{
var fullname = "myProp" + name;
return eval("obj."+fullname); // should just return obj[fullname]
}
Not sure why variations on this show up so often, but it makes me cringe whenever i see it - i try not to demonize eval()
, but this is just plain silly.
Not realizing javascript always uses floating point arithmetic leading to nasty surprises.
Infinity
, not an error/exception. Careful when looping through an expression resulting from a division.NaN
is poisonous (anything you do with it results in NaN) as in, Welcome back undefined, there are NaN items in your shopping cart
.Re-usability
my ever lasting mistake with javascript is that ridiculous notion that crosses my mind every time i create a new function: "im not gonna use that again, so no need for it to be reusable"! I usually come back to it in less than two days to reuse! lesson of the day: always think ahead when it comes to Javascript, its pretty expensive
the # pain
inspired by an answer above, using the # as a value for an href attribute just because you are relying on the onclick is a BIG BIG MISTAKE, # is a link to a named anchor, use a valid url and return false onclick
<a href="#" onclick="function(){...}">link</a> // mistake
<a href="http://myalturl-incase-js-is-off.htm" onclick="function(){return false}">link</a> // correct
or, same as above and on load, call a function to remove the href attribute
$("a.myanchor").attr("href","javascript://");
Think in portability and distribution, don't pollute the global scope.
When comparing variables, use the strict equality operators (===
and !==
) to add type cohesion:
1 == true // true
1 === true // false
0 == '' // true
0 === '' // false
Be careful with the falsy values: 0
, NaN
, ''
, null
and undefined
are all false in boolean expressions.
Don't use eval to parse JSON... use a JSON Parser...
Using client-side JavaScript to do things that should be done server-side.
Expanding native JavaScript objects should be avoided, although I don't consider it a mistake if used in affordable situations.
Array.prototype.func = function(){ ... };
trying to use setTimeout
this way :
setTimeout("myFunction('test', 8)", 500);
!
Using reserved words. Especially if you are coding to Firefox which is (mistakenly) very permissive about them. Some of the ones that have gotten me (sometimes more than once):
var int;
var export;
var import;
var float;
var abstract;
var public;
var class; // this one seems obvious but it happens often
var const;
A quick search for JavaScript reserved words will get you the whole list.
Don't use Javascript to replace CSS. That is my only golden rule when it comes to Javascript.
Suppose you've got some buttons in your HTML page. And you want to operate on them. Here's a small script block, that alerts the index number of the button relative to the DOM.
var btns = document.getElementsByTagName("button");
for( var i = 0; i < btns.length; i++ ){
// I know, I know, IE won't be running this bit. More on that later.
btns[i].addEventListener( "click", function (){
alert( "Hi, I'm button #" + i );
}, false );
}
Lovely. Let's say we've got 8 buttons. Click on the first one and it should say "Hi, I'm button #0" right? Hey, wait a second, it says #8? What? All the buttons say #8? What's going on?
What's going on is, that little i variable at the end is still in the scope you've set the event listener. And it's 8. Because you've been telling it to increment itself while you were busy setting events in a for loop and you told the for loop to quit if i was bigger than 7. So i sits there waiting for you to call it's scope, holding onto the last value it was assigned.
So what do we do? Closures! We create a small closure inside the for loop to capture the value of i and use it in our event.
var btns = document.getElementsByTagName("button");
for( var i = 0; i < btns.length; i++ ){
(function (index){
btns[index].addEventListener( "click", function (){
alert( "Hi, I'm button #" + index );
}, false );
})(i);
}
Instead of 1, now we've got 8 closures. And IE is leaking precious memory but, what the hey, we've got what we asked for, right?
Don't add unnecessary members to the Object prototype. This can S.E.R.I.O.U.S.L.Y slow down your application because everything inherits from object.
for example:
object.prototype.contains = function(membername, membervalue){
// some processing
}
could be better written like this:
objectContainsMember = function(object, membername, membervalue){
// some processing
}
Common mistake: Using Javascript arrays like hash tables or PHP-style of arrays.
var currentUploadsArray = [];
function hypotheticalUploadFunction()
{
currentUploadsArray.push('a');
currentUploadsArray.push('b');
currentUploadsArray.push('c');
//currentUploadsArray holds: ["a", "b", "c"]
// .... do stuff ...
delete currentUploadsArray['b'];
// returns true, but doesn't do anything.
// currentUploadsArray now still holds: ["a", "b", "c"]
// Why? because JS arrays are supposed to be numeric indexed only.
// Let's try to delete by index.
delete currentUploadsArray[1];
// Another gotcha:
// returns true, but doesn't remove the item from the array, it sets it to undefined:
// currentUploadsArray now holds: ["a", undefined, "c"]
// So now if you check for the length, it still says 3:
currentUploadsArray.length; // 3
delete currentUploadsArray[0];
delete currentUploadsArray[2];
currentUploadsArray.length; // Yep, still 3.
// This sucks because there's no easy way to see if the array is empty,
// thus making it useless for a lot of common things, like a queue.
}
Gotchas
Learn the language. Always do things for a reason. If you're not sure about something, go and see the specification.
A related thread: How to write high quality Javascript
JavaScript stops running because an exception is thrown and not caught. They are actually caught in the browser's code (mostly the event processing loops).
To avoid these unintended halts, use:
try {
alert( undefined.thisThrowsUndefinedException );
}
catch( ex ) {
alert( ex );
}
Most common mis-uses of javascript is imho coding for just one browser and not testing for the others.
And how to fix them -
IMHO there are no most common mis-uses of javascript, but there is a large list of re-occuring syntax, syntactic and other errors (probably leading to exceptions). But its hard to priotirize those. Point.
If you really want an answer, then either do a survey (e.g. community wiki on SO), or write a software, which finds and analyses those errors on a list of websites.
But, the list of mis-uses that finally lead to errors is really, really long.
If there'd be a list of common errors, then those would be fixed meanwhile on most sites.
Mistake: using ==
instead of ===
. Solution: use ===
JavaScript truth table:
'' == '0' //false
0 == '' //true
0 == '0' //true
false == 'false' //false
false == '0' //true
false == undefined //false
false == null //false
null == undefined //true
" \t\r\n" == 0 //true
Source: Doug Crockford
(=== returns false for all of these)
Mistake: using eval. Solution: don't. (you can use o[foo]
instead of eval("o."+foo)
Most common error or mis-use: You did not understood the language!
JavaScript may be seen as a language with a lot of gotchas (as is the case for most of us).
But it may also be seen as a language which is just different than most other languages we know!
Most importantly, the scope is different than what most people expect! A JavaScript programmer should start with learning how the scope works in JS. Then everything is enlightened.
The most common error, imo? Using JavaScript as a scripting language and not a Functional language.
Personally I stepped away from JS while I learned OO development, and returning to it with the awareness of what a Functional language is, and how it can be used, made me furious to find so many entrants avoiding the functional aspect of it (which is a very significant portion!).
So, while your question might be better asked as "how do I avoid syntactical erros and their effects?", just remember that not all errors are caught by a compiler/parser/interpreter--perhaps you can avoid some of these errors by focuses on how to best leverage the language.
Mistake: relying on the existence of some global object property without testing to see if it exists first.
Not all browsers/environments implement the same API.
With things like window.alert() you can get away with using it, because it's pretty safe that it will always be supported. Other things, however, are not so widely supported, like (window.)document.all, window.returnValue, etc.
What about using duck typing or feature detection, this is very helpful when insterested in handling something depending on the browser.
for example:
if(object.property)
or:
if(object.function) / if(object.prototype.member)
not taking this into consideration might cause some headaches sometimes, so this would be a way to avoid it and a good practice.
Expecting this
to pass a reference to the a
clicked when used in
<a href="javascript:somefunc(this);">do something</a>
As Josh Stodola explains in this answer, the correct way to pass a reference in such cases is to use onclick
instead of href
:
<a href="javascript:void(0);" onclick="somefunc(this);">do something</a>