Thursday, November 24, 2011

Local Variables and Variable Hoisting

The other day I ran across a few interesting features of JavaScript variable scopes.  Consider this code:


function doSomething({
    var 1;
    if (true{
        var 2;
        alert(a);
    }
    
    alert(a);
}

doSomething();



What would you expect?  If you are used to coding in a C like language, you would expect the first alert to be 2 and the second to be 1.  In JavaScript however this is not the result.  We get 2 for both alerts.  How come?


It turns out that JavaScript has no concept of "block-level" scoping (specific to the if block in this example).  Variables are either globally scoped or they are function scoped.  That means that even though we are in an if-block, we are still using the same scope as we were before entering the block.  So in our example the variable "a" is re-declared and assigned to 2, and it continues to be in scope after leaving the function, and will still be available until the function ends.


Here is a jsFiddle if you would like to play with it.


Reading about this led me to another interesting topic concerning variable scopes.  Look at this code:


1;
function doSomething({
    alert(a);
    var 2;
    alert(a);
}

doSomething();



To start we have a global variable named a which is assigned the value of 1.  Then when doSomething is called the alert displays the global variables value, a function level variable is created and assigned 2, which is then alerted.  Right?  Nope...


The first alert displays "undefined" while the second is 2.  Why doesn't the first fall back to the global variable?  This is due to the way JavaScript handles declarations.  They are automatically "hoisted" to the top of the function, leaving code that looks would look like this:


1;
function doSomething({
    var a;

    alert(a);
    2;
    alert(a);
}

doSomething();




Now it makes sense why we would get an "undefined".  The function scoped variable is completely hiding the global one, and it hasn't been assigned yet.  This is an important point... declarations are hoisted but assignments are not.  


Again, here is a jsFiddle with the scenario.  


Ben Cherry also has a good blog post about this issue.  






Wednesday, November 2, 2011

The debugger statement

The debugger statement in JavaScript will cause a debugger (like FireBug or the Chrome developer tools) to pause execution just as if there was a breakpoint at that line.  This is most useful when you have dynamically generated code or whenever you don't have a chance to set a breakpoint on a line before the code executes.  

Simple example:
var a = 44;
debugger;
alert(a);

In this case the debugger (if open) will pause before the alert is shown.  

Here are the MDN docs for the debugger statement.

Tuesday, August 23, 2011

JavaScript Strict Mode


Many of us have been taught that global variables are evil.  They contribute to code which is difficult to maintain.  The JavaScript language, however, allows global variables and they are quite common.  Luckily JavaScript has a lesser known mode called “strict mode” which will throw an error if we try to create a global variable.

To use strict mode you begin your script files with “use strict”; The quotes are needed also.  This will make all code inside this JavaScript file run under strict mode.  Any functions called from within this file will also run in strict mode.  Here is a quick example of code that allows global variables because it is not in strict mode.  And here is the same code which throws an error when trying to create a global variable. 

Strict mode tightens down a few other language features, most of which you won’t/shouldn’t care about anyway.  For instance normally you can have a function with this signature:

  function sum(x, x){};

Why you would want two parameters to have the same name, or how you would expect the code to work… I don’t know.  But strict mode throws an error if you try.  I consider that a good thing. 

For more info check out the MDN page about strict mode.

Friday, July 29, 2011

All about function arguments

JavaScript allows a variable number of arguments to be passed into a function.  Thats actually a pretty cool feature that some languages (Java) have only added in the last few versions.  Lets look at it a little more by examining a function that takes two numbers and returns the sum:

function sum(firstNum, secondNum) {
return firstNum + secondNum;
}

We can call this function like this:

// Will return 14
sum(3, 11); 

But we are allowed to pass as many arguments to the function as we want:

// Will still return 14
sum(3, 11, 4, 99, 0, 2);

No errors are thrown, however the function only adds the first two numbers, as we coded it to do.  How can we take advantage of a variable number of arguments?  This is where the arguments object comes in.  It is available to you any time you are executing code within a function.  Its an array like object that contains each of the arguments that were passed to the current function.  Not just the declared parameters... but everything that was actually passed at call time.

Lets see how we can make our sum function work better using the arguments object:

function sum() {
var runningTotal = 0;
for (var i = 0; i < arguments.length; i++) {
runningTotal += arguments[i];
}
return runningTotal;
}

We now loop through each of the arguments adding them in turn.

// Will return 14
sum(3, 11);


// Will return 119
sum(3, 11, 4, 99, 0, 2);

So now that we can determine our arguments at call time, is there an easy way to find out (with code) how many declared parameters a function is expecting?  Yup.  Simply check the length property of the function.  Example:

function takesThree(x, y, z) {}
console.log(takesThree.length);
--> 3


function takesOne(first){}
console.log(takesOne.length);
--> 1

function takesNone(){}
console.log(takesNone.length);
--> 0


Friday, July 22, 2011

When do I use the === operator in JavaScript?

As new JavaScript programmers spend time with the language they often run into an operator they are unfamiliar with.  Most of us are familiar with the double equals (==) operator.  But what does the triple equals (===) operator, aka strict equals operator, do?  Why would we use it over ==?

Let me begin by explaining how the == operator works and then contrast the === operator.  Suppose I have the following code:
     var aNum = 3;
     var aStr = “3”;
     console.log(aNum == aStr);
     // output is true

Even though aNum is a number and aStr is a string… the variables are considered equal by the == operator.  This is because == will attempt to convert the variables to the same type before comparing them.  As long as you are aware that happens, it is usually OK, and sometimes helpful. 

Now let’s look at the same code with the === operator:
     var aNum = 3;
     var aStr = “3”;
     console.log(aNum === aStr);
     // output is false

The === operator makes no attempt to convert the variables before comparing, so of course the variables are not equal. 

To summarize, any time you are doing an equality comparison when variable type does matter, use the === operator.  If you don’t care what the variable’s type is, or specifically want type coercion, use the == operator.  

Saturday, July 16, 2011

How do I convert a String to a Number in JavaScript?

Variables in JavaScript are not strongly typed.  So if I have a variable named x:
       var x;

I can assign anything I want to it:
       X = 4; // legal
       X = “a string”;  // also legal
       X = {width: 45, height: 30};  // still legal

This can be nice at times… but if we aren't careful it can also introduce bugs.  For instance:
       var width = “3”;
       width += 7;
       // width now equals “37”, not 10

Cases like this require us to first convert the string to a number.  There are three methods of doing this which I will discuss:

  • The unary + operator
  • The global function parseInt()
  • The global function parseFloat()

If you know the string you are converting only represents a number (e.g. “3” or “5.9”, not “34px”) you can use the unary + operator.  Simply place a “+” right before string, and you now have a number.  Example:
       var width = “3”;
       var height = 9;
       var area = height * (+width);
       // area now equals 27

Note that you are not adding anything to the variable (that would be a binary operator), you are simply making it a number.  Again, this only works with a string that represents a number.  The following will give NaN (not a number):
       var width = +“5px”;
       // width now equals NaN

So, what to do with “5px”?  Do we have to remove the non-numeric characters first?  Nope.  The parseInt function helps us out here.  It will return an integer based on the first number it finds.  So the previous failure would now look like this:
       var width = parseInt(“5px”);
       // width now equals 5

The non-numeric characters after the number are ignored.  Note that non-numeric characters found before the number will cause a NaN to be returned.   Example:
       var width = parseInt(“w 5px”);
       // width now equals NaN

FYI, you can also pass a second parameter to the function which is the radix of the first argument.  Example:
       var x = parseInt("10010", 2);
       // x now equals 18

This works great, but what if we want a floating point number?  The global function parseFloat will do very similar work, returning a floating point number instead.  Example:
       var pi = parseFloat(“3.14”);
       // pi now equals 3.14

The same rules apply about having characters before and after as do to the parseInt function. 
       var pi = parseFloat(“3.14 degrees”);
       // pi now equals 3.14
       var pi = parseFloat(“x 3.14 degrees”);
       // pi now equals NaN

The parseFloat function can also take a string in scientific notation:
       var x = parseFloat("3.14e5");
       x now equals 314000

Finally a quick way of converting back to a String is as follows:
       var score = String(3722);
       // score now is a string of “3722”
       var label = “Your high score is “ + score;

As this demonstrates, conversion to a String is not needed as frequently because any time we add a string to a number… we get a string concatenation not a numeric addition. 

Bonus material:
There are two more global functions which may be helpful in some cases:
       isNaN()
       isFinite()

isNaN() will return true if the variable currently is NaN (not a number).  isFinite() will return false if the argument passed in is positive infinity, negative infinity, or NaN. 
   // will return true (because x is 3)
   isFinite(3 / 1);  
   // will return false (because x is positive infinity)
   isFinite(3 / 0);  
   // will return false
   isNaN(4);  
   // will still return false because
   // “4” can easily be converted to a number
   isNaN(“4”);  
   // will return true because the string can’t 
   // be easily converted to a number.
   isNaN(“4px”);  

Saturday, July 9, 2011

How do you make an object final in JavaScript?

­In Java, adding the final modifier to a class prevents the class from being extended.  C# has a similar keyword in sealed.  JavaScript, however, does not have a direct equivalent of this functionality... but its latest version does have some related capabilities. 

There are three functions which provide varying levels of control: 
  • Object.freeze(obj)
  • Object.seal(obj)
  • Object.preventExtensions(obj)

Passing an object into Object.freeze() will keep any new properties (or “methods”, which really are properties) from being added, no existing properties can be deleted, and the values of existing properties can’t be changed. 

Example:
  var x = {height:3, width: 4};
  Object.freeze(x);
  x.color = “red”; // may throw a TypeError exception
  delete(x.width);  // may throw a TypeError or return false
  x.height = 7;  // may throw a TypeError exception
  // x is still {height:3, width:4}



Object.seal() is similar in that no new properties can be added to the object, no existing properties can be deleted… but the values of existing data properties CAN be changed. 

Example:
  var x = {height:3, width: 4};
  Object.seal(x);
  x.color = “red”;  // may throw a TypeError exception
  delete(x.width);  // may throw a TypeError or return false
  x.height = 7;
  // x now is {height:7, width:4}



Finally the least restrictive function is Object.preventExtensions().  It will prevent any new properties from being added, but existing properties can be deleted or modified as normal. 

Example:
  var x = {height:3, width: 4};
  Object.preventExtensions(x);
  x.color = “red”;  // may throw a TypeError exception
  delete(x.width); 
  x.height = 7;
  // x now is {height:7}



These functions are new to the latest version of JavaScript, so they are only available in Firefox 4+, Chrome 6+, and IE 9+.  As of Safari 5 and Opera 11.5, the functions are not yet supported.