egqyaq
Last Updated: February 25, 2016
·
1.578K
· jjperezaguinaga
Me 180x180

Debugging Javascript Applications

Picture

Let's face it, if you don't know how to use Chrome Developer Tools or Firebug, you can hardly call yourself a web developer. However, despite all their amazing features, sometimes both tools fall short solving complex bugs when multiple libraries are involved: they have nested function calls that you don't know where they are or switch the value of 'this'!

In order to solve these sort of bugs, we need alternative strategies to help squash even the meanest bug around town. In this article we will discuss non-common ways and techniques to solve bugs that are hard to fix.

Problem: Nested functions A.K.A. Who's calling me?

Imagine you have a function X that get's called every once and then with a parameter Y. Most of the time, Y is what you are expecting, but then, in one specific case, it's undefined. You know that code is triggered by function A in your super awesome library, yet function A calls multiple functions that can go as far as function B, function C and so on, until they finally hit your function.

If the code is obvious and you are good with the Chrome Dev Tools, you can use breakpoints to start tracing down the problem. However, if the functions are really nested and the flow of the calling is long, you might take a while before you can actually find a good place to breakpoint. In the other hand, if you have access to the function being used by the library A, then use caller:

function X(Y){
    console.log(X.caller);
}

The caller method will tell you which function is calling your function. If the functions is anonymous, you can use the word this, given that the function context was not overridden. Other way to do this is through in case the function was not created with a new operator is through arguments.callee, so the same as before would be:

function X(Y){
    console.log(arguments.callee.caller);
}

Be wary that arguments.callee is not fully supported, and also discouraged. Can be part of your development code, but shouldn't even be in staging.

Problem: Undefined/wrong values A.K.A. What's the value of this?

With all the context switching Javacript can have and it's lack of scope, you could had been in a situation where the value of a variable is not what you were expecting it to be. One of my favorite examples is this one.

var count = 0; 
for ( var i = 0; i < 4; i++ ) { 
  setTimeout(function(){ 
    console.log("Value of i should be equal to count"); 
    console.log("Value of i:"+i);
    console.log("value of count:"+ ++count);
  }, i * 200); 
}

The value of i is always 4. Why? Because the closure doesn't work anymore due the change of context made by the setTimeout. This will fix it:

var count = 0; 
    for ( var i = 0; i < 4; i++ ) { (function(i){
        setTimeout(function(){ 
        console.log("Value of i should be equal to count"); 
        console.log("Value of i:"+i);
        console.log("value of count:"+ count++);
      }, i * 200);  
    })(i)
}

Sometimes problems are not that subtle. Now, imagine this code has fallen in your hands:

var town = { 
  name: "Christmas Town", 
  king: "Santa", 
  whosTheKing: function() { 
    var town = this.name; 
    var king = this.king; 
    var getKing = function() { 
      return this.king 
    }(); 
    return getKing + " is the king of "+ town 
  }
}

(If code like this actually falls in your hands, I feel sorry for you. This code comes from my Protip about the keyword this)

Run town.whosTheKing() and you get "undefined is the king of Christmas Town". Bananas. What's going on here? If you read my tip, then you would know that Javascript is breaking the reference to the this word, but otherwise, how could you debug this? .call to the rescue.

.call changes the value of the this object to whatever is given. In this case, we want to see that some properties are properly tied. For example:

town.whosTheKing.call({name:"Gotham City", king: "Batman"}) // returns "undefined is the king of Gotham City"

With this test, we now know that the method is not using the proper this value. Where is the value of king then? Let's use the window object now.

town.whosTheKing.call(window) // returns "undefined is the king of "

The properties are declared though, so they have to be stored somewhere. Could it be the global head object?

window.name = "Arkham Asylum";
window.king = "The Joker";
town.whosTheKing.call(window) // returns "The Joker is the king of Arkham Asylum"   

Aha! So they are indeed being stored at the window object, aka the global head object. Although it's a silly example, this could had probably been the pattern people figure it out that the reference was being broken when 2 nested levels of context were given.

Conclusion

Both .caller and .call are extremely useful native javascript commands that can help you debug your applications. If properly used, they can even form part of your code in order to construct interesting design patterns. Use them in conjunction with your current debug toolset and start squashing bugs.

Say Thanks
Respond

1 Response
Add your response

4709
D42a7264714dee5006b9c99d2567a320

I find doing console.error('X called', arguments) more useful than console.log(X.caller); in the case from your first example, because you often want to inspect the whole stack trace, not the immediate caller of the function in question (especially if you use libraries).

over 1 year ago ·