39

I'm planing on giving an introduction talk on JavaScript and in the preparation process I wondered what the top pitfalls are that rookies fall into.

I know I've had a few gotchas before I fully understood closure, but much of the strange behavior in JavaScript is not something I think about any more...

So, which pitfalls should you definitely point out to the rookies?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
googletorp
  • 33,075
  • 15
  • 67
  • 82

13 Answers13

37

Boolean type conversion.

''        ==   '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

As well as the difference between null and undefined. As listed in the table above, comparing null & undefined with == returns true, but with === it returns false. This behavior makes sense once you understand that undefined is very different from a variable having a null value, and something holding the value undefined is different from something being undefined.

Community
  • 1
  • 1
Dan Herbert
  • 99,428
  • 48
  • 189
  • 219
29

Don't accidentally leave a trailing comma in an object definition literal or IE will fail and you won't notice until much later because you never use IE for development and by then it could suck figuring out what happened.

var foo = { 
    bar: "bar", 
    baz: "baz", 
};

Note @JulianR's comment: In arrays, IE doesn't fail directly by throwing some syntax error, but will fail when you try to use the array because the added comma makes IE think there's one more element in the array, with value undefined, than there actually is. So if you ever have an error because for some reason the last element in an array is undefined: it's a comma.

blubb
  • 9,510
  • 3
  • 40
  • 82
Ben Zotto
  • 70,108
  • 23
  • 141
  • 204
  • 10
    +1 Very recognizable this one. In arrays, IE doesn't fail directly by throwing some syntax error, but will fail when you try to use the array because the added comma makes IE think there's one more element in the array, with value `undefined`, than there actually is. So if you ever have an error because for some reason the last element in an array is `undefined`: it's a comma. – JulianR May 01 '10 at 16:12
  • 1
    +1 Excellent comment, this is a very nasty pitfall. – dv_ Jul 28 '12 at 11:05
23

Admittedly I've been guilty of some of these in the past, for your amusement it's the ones in bold:

  • Not knowing incorrect (and the very few correct) uses of eval
    eval("obj."+prop);
  • Using with statements
  • Using parseInt(str, base) without specifying the base argument.
  • Using this in timer/callback functions.
  • Using eval-like expressions in timers
    setTimeout("someFunc(myScopedVarWhoops)");
  • Thinking jQuery is the name of the language you're coding
  • Performing simple JavaScript tasks using a framework -- $(1).plus(1) anyone? ;-)
  • Using continue without incrementing or adjusting the conditional variable.
  • Flooding the global namespace with variables
  • Forgetting var in or before for statements. for (i=0;i<10;i++)
  • Using an obfuscator and just letting it run wild on your code
  • Not really a pitfall, but pointless - return condition ? true : false; instead of return condition;
  • Not commenting your code, applies to all languages really.
  • Using try...catch...finally statements to catch errors instead of using if statements to check variables.
  • Foolishly attempting to stop "view source" by blocking right mouse clicks on your pages (I was young *sobs*!)
  • Using { 0: "Foo", 1:"Bar", 2:"Foobar" } instead of [ "Foo", "Bar", "Foobar" ]
  • Using parseInt() on user input
    parseInt("1,000") // -> 1, wrong!
    +"1,000" // -> NaN, correct!

Some already mentioned:

  • Not using strict equality (===) operators whenever possible
  • Setting event handlers to the return value of a function instead of a reference to said function
  • Not ; terminating statements properly
  • Using for...in loops on arrays

Might think of some more after I've slept :-)

Andy E
  • 338,112
  • 86
  • 474
  • 445
  • Why shouldn't you use `this` in callbacks? – Lèse majesté Feb 18 '11 at 04:11
  • 1
    @Lèse: because callback functions, particularly ones used with timers, don't normally have a context. – Andy E Feb 18 '11 at 11:07
  • @Andy E: What if you're assigning a callback to an event listener? – Lèse majesté Feb 18 '11 at 13:58
  • 1
    @Lèse: naturally, those are the exceptions, though they are referred to more as event listeners or event handlers than callback functions (even though they are callback functions). The confusion often stems from using `this` inside an event listener function, a lot of people think that `this` remains the same for functions defined in the same scope. – Andy E Feb 18 '11 at 14:06
  • These links I found might clarify some of the other pitfalls for those unfamiliar with them: http://xkr.us/js/eval and http://dev.opera.com/articles/view/efficient-javascript/?page=2 – Lèse majesté Feb 18 '11 at 14:38
  • "for..in" loops can be used safely with "hasOwnProperty": for(var i in data) if( data.hasOwnProperty(i) ) ... – vsync May 25 '11 at 23:21
  • @vsync: inherited properties aren't the only reason not to use `for...in` on arrays — order of iteration is not guaranteed. Stick to standard `for` and you're in control of your loop. – Andy E May 26 '11 at 07:36
  • @Andy E: so how does the order of iteration is performed? what's the logic behind the "for...in" if it's so shaky ? – vsync May 26 '11 at 08:28
  • @vsync: the logic behind it is "implementation dependant", which means the vendor writes their own logic (which may or may not be disclosed). It's common knowledge that at least [Google Chrome differs from other browsers](http://code.google.com/p/chromium/issues/detail?id=883) on this logic. – Andy E May 26 '11 at 08:59
  • @Andy E: I read through it, and this issue is a case only for Objects, not Arrays.. which they have a good argue why it's useless the have order for Objects anyway. – vsync May 26 '11 at 10:16
  • @vsync: in JavaScript arrays **are** objects. A `for...in` loop is specifically designed for looping over object properties. In arrays, each member has a numerical index property name, starting at 0 (as you may know). – Andy E May 26 '11 at 10:21
  • @Andy E: read the discussion. I know Arrays are Objects, but I meant {} objects. for [] Arrays , the order is implemented correctly, they mention this in the Discussion – vsync May 26 '11 at 10:27
  • @vsync: The discussion doesn't indicate you know the difference. My point was there is no "correct" order as far as the specification goes. There is no guarantee that all JS environments will behave the same way, and that is just another reason why you should not use `for...in` on arrays. Anybody who wants to take the risk and ignore such advice is perfectly welcome, yet it very much remains a "pitfall" IMHO. – Andy E May 26 '11 at 14:34
  • @vsync: [case in point](http://stackoverflow.com/questions/6141368/javascript-script-not-working-in-ie/6141513#6141513). Small difference; it's a HTMLCollection and not an Array, but most view them as more or less the same thing but with different prototypes. – Andy E May 26 '11 at 16:01
15
  • Forgetting to declare variables with var
  • Misunderstanding (or not understanding) variable scope and closures
  • Trying to solve nasty compatibility problems that framework teams have already solved
Pointy
  • 405,095
  • 59
  • 585
  • 614
  • 4
    Definitely the frameworks. By all means, someone should understand JS before using one. But they should know they're there. – detly May 01 '10 at 12:51
  • Yeah, frameworks can be a big pitfall. This site is stuffed with questions from newbies trying to do something specific with jQuery. jQuery has a function that almost does the task, or do some of the task, but it is just too automagic to be more generally useful. – aaaaaaaaaaaa May 01 '10 at 16:00
14

+ to concatenate strings:

var a = '2';
var b = 3;

a * b #  6
a - b # -1
a + b #  23
Kobi
  • 135,331
  • 41
  • 252
  • 292
9

JavaScript strings are not byte strings, nor are they even Unicode strings. They are UTF-16 strings. Most characters, including international ones, are a single element in a JavaScript string. But some characters (including most emojis) fall outside the Basic Multilingual Plane and thus appear as two elements in a JavaScript string.

Example:

> "♫".length
1
> "".length
2
> "".charAt(0)
"\uD800"
> "".charAt(1)
"\uDF08"
> "" === "\uD800\uDF08"
true
Joey Adams
  • 41,996
  • 18
  • 86
  • 115
  • 4
    Just to nitpick: there's nothing wrong with having UTF-16 strings, what's wrong is having leaky abstractions (like Javascript, .NET, Java, etc.etc.) an example of a sane Text API backed by UTF-16 is Haskell's Data.Text: `(T.length $ T.pack "") == 1` returns `True` – berdario Oct 29 '13 at 20:30
  • 1
    UTF-16 is a way to encode unicode. So yes, they are unicode strings. – Joeri Hendrickx May 07 '17 at 21:12
  • @berdario: **cough** http://utf8everywhere.org/ **cough** ^^ – Olivier Dulac Jun 14 '17 at 15:36
  • I rejected the edit to change "Unicode strings" to "UTF-8 strings". What I'm pointing out here is that a JavaScript string is not a sequence of Unicode codepoints. Each "array element" is a UTF-16 codepoint, meaning a surrogate pair is represented as two "characters" instead of one. – Joey Adams Jun 04 '20 at 13:36
7

The biggest difficulties I see for the beginner are understanding execution context (i.e., what "this" means whenever and wherever it is encountered) and the prototype system of inheritance.

Robusto
  • 31,447
  • 8
  • 56
  • 77
5
  • Closures - otherwise known as lambda functions - watch out for memory leaks.
  • Browser differences, testing in both Internet Explorer and at least one other browser is a must. Functions that only work in some browsers, or work differently in different browsers should generally be avoided. If this is not possible browser specific branching is better done detecting browser features instead of browser versions. This increases the chance of the code working in future browsers and browsers that have not been tested.
  • Getting too caught up in jQuery or Ajax framework abstraction, and not knowing the underlining JavaScript well enough to know how to fix framework issues.
  • Not knowing that JavaScript can be used to some degree to write OOP code. In fact it can give you a very basic OOP framework with objects.
  • Case sensitivity (if you're a VB.NET developer)
  • IP protection - knowing that you can obfuscate JavaScript, but the source code you put out there will be very easy to steal and reverse engineer. This might not even be an issue depending on the complexity of the client-side application you're writing.

I can't think of any more, but I hope this helps.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
JL.
  • 78,954
  • 126
  • 311
  • 459
  • 13
    Browser detection is evil. You should use feature detection, not browser detection. – el.pescado - нет войне May 01 '10 at 13:09
  • 1
    @el.pescado: +1, but to be fair, sometimes you need to detect behavior, which isn't as easy as detecting features and objects. – outis May 01 '10 at 13:25
  • 1
    "Closures - otherwise known as lambda functions...". Not true - closures can be lambdas, but don't have to be (they can also be named functions). –  May 01 '10 at 13:49
  • 3
    @el.pescado: Browser detection isn't evil, it's merely ineffective and error-prone. – Robusto May 01 '10 at 14:42
  • To address the comments about browser vs feature detection: This isn't incredibly hard to do. jQuery provides very good support for handling this (http://api.jquery.com/jQuery.support/). If you don't want to use jQuery, I would still recommend checking out the jQuery.support source code as it's very easy to implement on your own as needed. (http://github.com/jquery/jquery/blob/master/src/support.js) – Dan Herbert May 01 '10 at 15:50
  • Ineffective and error-prone === Evil – aaaaaaaaaaaa May 01 '10 at 16:09
  • Can someone elaborate on the memory leak comment? What is the connection to closures? – Lèse majesté Feb 18 '11 at 03:53
  • @Lèsemajesté Probably, he was talking about IE. I don't remember where, but there even was a MSDN article saying you should avoid closures (talking about them as a bad thing!) because they ***caused*** memory leaks. As we all know this isn't true, it's just IE that sucks. So my advice is: don't give a damn if the user using IE has a memory leak, he'll not notice anyway. Of course you should make an app fast, but make it fast considering best practices in Chrome, Firefox & co. – Camilo Martin Dec 17 '12 at 23:25
  • @Camilo: Upon further research, it seems that Firefox is also prone to the same circular reference memory leaks because both IE and FF use reference counting to perform garbage collection on DOM objects. The problem with closures arises when you create a JS object as a reference to a DOM object in an outer function (`var obj = document.getElementById(...);`) and then create an inner function that gets assigned to one of that DOM object's properties (e.g. `obj.onclick = function() {...};`). The closure creates an implicit reference from the DOM object back to the JS `obj` object. – Lèse majesté Dec 18 '12 at 01:18
  • @Lèsemajesté Is that still true with current versions of Firefox? (The behavior looks correct, but feels wrong...) Also, I suppose I'd be safe if I only use `addEventListener`, right? – Camilo Martin Dec 18 '12 at 08:00
  • @Camilo: TBH, I'm not sure. I've seen claims of the behavior only affecting IE7 and earlier, but I've never tested it on the current versions of IE or FF. Using `addEventListener` probably doesn't matter much. What matters is where you define the callback function. If you define it within the closure, then it's still going to preserve a reference to the DOM object. The solution suggested in an IBM article I read is to just set `obj` to null at the end of the outer function. – Lèse majesté Dec 20 '12 at 01:07
4
  • Using window.onload = init(); instead of window.onload = init;
  • Boolean equivalences (as mentioned already)
  • Closures within a loop.
  • Using for in loop variant for iterating over Arrays.
  • Not using ; because it's "optional".
  • this (just... in general :))
  • Not using var
  • Knowing that obj.ref === obj["ref"]
Matt
  • 74,352
  • 26
  • 153
  • 180
4
  • Creating sites that don't work without JavaScript
  • Using JavaScript for things that should be done server-side
  • Using frameworks for simple tasks that don't require them
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
RoToRa
  • 37,635
  • 12
  • 69
  • 105
3

Not a real coding pitfall, but more one of general thought:
Don't trust the things your JavaScript is doing, it might have been turned off or even monkey patched. That means never rely on client-side validation. NEVER.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ivo Wetzel
  • 46,459
  • 16
  • 98
  • 112
3

The whole concept of prototyping takes some time to fully understand but here are some common pitfalls:

Forgetting to reset the constructor property after assigning a prototype object:

var Foo() = function ()
{
    this.batz = '...';
};
Foo.prototype = new Bar();
Foo.prototype.constructor = Foo;

If you forget the least line, new Foo() will actually execute Bar().

Another pitfall with prototyping is iterating over objects/arrays without filtering out the members of the prototype:

for (var i in obj) {
    if (obj.hasOwnProperty(i)) {
        //stuff...
    }
}

The extra condition will skip any members that are inherited from the prototype of obj.

selfawaresoup
  • 15,473
  • 7
  • 36
  • 47
2
typeof null is object


>>> var i = 1 + undefined; i;
NaN
>>> var i = 1 + null; i;
1
nandin
  • 2,549
  • 5
  • 23
  • 27