Liam Kaufman

Software Developer and Entrepreneur

Common JavaScript Errors

With the rise of thick client-side applications, and Node.js, JavaScript has become an increasingly important programming language. Despite its simplicity, JavaScript presents some difficulties for those new to the language. I thought it would be useful to outline several JavaScript errors that I commonly made when I was learning the language.

Scope: this, that and window

Like many programming languages JavaScript provides internal access to objects using the keyword this. Unfortunately, what this refers to differs depending on what called the function containing this. A common example, shown below, is what happens when setTimeout is called.

In the example below a Dog object is created, with a bark function. The Dog ralph is instantiated with the name ‘Ralph’ and when ralph.bark() is called, “Ralph” is printed to the console.

What becomes confusing is what happens when setTimeout is called with the parameters ralph.bark and 500. After 500 milliseconds ralph.bark is called, however, nothing is printed to the console.

The this problem
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var Dog = function( name ){
  this.name = name;
}

Dog.prototype.bark = function(){
  console.log( this.name );
}

var ralph = new Dog('Ralph');

ralph.bark()
// Ralph is printed to the console

setTimeout( ralph.bark , 500 );
// nothing is printed to the console

Mozilla Developer Network refers to the problem above as ‘The ”this” problem’. What happens is this within bark() refers to the browser’s window variable when bark() is called from setTimeout.

Avoiding the this problem.

Solutions to the this problem.
1
2
3
4
5
6
7
8
9
10
11
// Works in JavaScript 1.8.5
setTimeout( ralph.bark.bind(ralph), 500 );

// using jQuery
setTimeout( $.proxy( ralph.bark, ralph ), 500 );

// using undescore.js
setTimeout( _.bind( ralph.bark, ralph ), 500 );

// using an anonymous function
setTimeout( function(){ ralph.bark(); }, 500 );

In each of the above the bind, or proxy functions explicity ensure that this within ralph.bark refers to ralph and not window. In the final example an anonymous function is called and provides another way of fixing the this problem.

Callbacks in Loops

When I launched Understoodit.com in May I included a waitlist for interested users to signup. I was planning on inviting a few dozen users a day, however, due to a deluge of emails sending out invites was delayed by a week or two.

To send the invites out, I went to Understoodit’s admin panel and seleted 40 people on the waiting list and clicked invite. A few days later I noticed that only a few individuals had accepted the invite. I looked at Postmark’s logs and noticed that invites were only sent to 5 individuals. What’s more those 5 individuals had received anywhere from 5 - 15 emails each. Meanwhile, the other 35 invitees had received no emails. The bug: I had a callback in a loop that iterated over all the selected invities and 1) called databaseModule.addInvitedUser, which created an invite token and added that invite to the database and 2) sent an email with the newly created token. Below is a simplification of the code, with error handling removed.

1
2
3
4
5
6
7
8
9
10
function sendInviteEmails( emails ){
  for(var i = 0; i < emails.length; i++ ){

    databaseModule.addInvitedUser( emails[i], function( error, token ){

      emailModule.sendInvite( emails[i], token );

    });
  }
}

What went wrong was that the anonymous function “captures the variable i, not its value”. The value of i is dependent on when the anonymous function is called, which varries depending on how long it takes to add the invited user to the database.

The solution I used was to wrap the anonymous function with an immediately invoked function expression (IIFE). The IIFE “locks” in the value of i ensuring that emailModule.sendInvite() refers to a different value of i on each IIFE call. Alternatively, one could create a second function outside of the loop and then call that (see option 2), a solution that would likely be easier to read.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// option 1
function sendInviteEmails( emails ){
  for(var i = 0; i < emails.length; i++ ){

    (function(email){
      databaseModule.addInvitedUser( email, function( error, token ){
        emailModule.sendInvite( email, token );
      });
    })( emails[i] );

  }
}

// option 2
function sendOneInviteEmail( email ){
  databaseModule.addInvitedUser( email, function( error, token ){
    emailModule.sendInvite( email, token );
  });
}

function sendManyInviteEmails( emails ){
  for(var i = 0; i < emails.length; i++ ){
    sendOneInviteEmail( emails[i] );
  }
}

The above quirk seems similar to a fairly common JavaScript interview question that takes the form of adding event listeners to an array of links.

Global Variables

The problems with global variables have been discussed many times before. Suffice it to say that you should avoid them by using var (e.g. var x = 0; vs x = 0;) when first declearing a variable. If you have a variable that has unaccounted for properties or values, there’s a chance that a global variable could be to blame.

I’d highly recommend defining all your variables at the top of the function to make it as clear as possible when a var is missing. Furthermore I’d recommend using JSHint which can warn you of global variables.

Values in HTML forms

A drop down menu
1
2
3
4
5
<select id="order-sizes">
  <option value="1">Small</option>
  <option value="2">Medium</option>
  <option value="3">Large</option>
</select>

When it comes time to get the form’s values, and use them within an application, I usually do something like:

1
2
3
4
5
var orderSize = $('#order-sizes option:selected').val();

if( orderSize === 1 ){
  console.log('Thanks for ordering a small!');
}

Unfortunately the value that jQuery returns is a String, and comparing it to 1, a Number returns false. Here are several solutions to this:

1
2
3
4
5
6
7
8
9
10
if( Number( orderSize ) === 1 ){ ... }

if( parseInt( orderSize ) === 1 ){ ... }

if( orderSize == 1 ){ ... }

// last solution
var orderSize = Number( $('#order-sizes option:selected').val() );

if( orderSize === 1 ){ ... }

In the first two examples orderSize is explicitly converted to a Number using the Number constructor and the global parseInt function. In the third example the double equals coerces orderSize to a Number before comparing to 1 (MDN on comparison operators). However, I’d recommend going with the last approach, which allows you to use orderSize as a Number in multiple spots without having to repeatedly caste to a number. If you don’t like the last approach I’d recommend the first or second approach, since it seems to be generally preferred to use the strict equal (tripple equal signs) and not to use the double equal sign.

Conclusions

Many of the above errors can be avoided by following JavaScript style guides (Google JavaScript Style Guide, Addy Osmani on Style Guides). A sign of how tricky JavaScript is comes from Github’s JavaScript Style guide that goes as far to recommend avoiding JavaScript altogether and using CoffeeScript - a suggestion a few would disagree with!

Comments