18 Nov 2010

You can ask anyone that I used to be quite sceptical on building sites that depend on the use of JavaScript. Time has not stood still however.

Enterprises like Google and Yahoo created JavaScript heavy web applications, JavaScript libraries like jQuery made handling JavaScript a lot less horrible, and last but not least browsers got better and faster JavaScript compilers. The conclusion can only be that a modern webdeveloper can't work without knowing the basic JavaScript quirks.

The basic JavaScript datatypes:

typeof(42) // number
typeof(true) // boolean
typeof("42") // string

Object is also a JavaScript datatype, and functions are first-class objects in JavaScript:

function fuz(){};
typeof(fuz) // function

function Foo(){ bar: "baz" };
var foo = new Foo();
typeof(foo) // object

And there are the two special cases for undefined and null. Consider the following examples, with the non-existing element with id MAIN.

var bap;
typeof(bap) // undefined

bap = document.getElementById("MAIN");
typeof(bap) // object, but the contents are null
bap.click(); // bap is null error

Consider the following example of how many = signs you can append in JavaScript, using string and number literals:assignment, equality, identity

var i = "42";

if( i == "42" ) {}
if( i == "43" ) {}
if( i == 42 ) {}

if( i === "42" ) {}
if( i === 42 ) {}

The first line (with one =) assigns the value of "42" to the variable i.The lines with == test for equality. Obviously, the string "42" is equal to the string "42" and not to the string "43". But because of automatic type coercion, the number 42 is also equal to the string "42".Using === it is tested if variables are identical. So in that case the number 42 is not identical to the string "42".

This is an anonymous function:

x.onclick = function() { alert("CLICK"); }

An anonymous function can only be called from where it is defined, because it is not referenced by a functionname. So it is a way to prevent reuse of a function. In this case, it's not possible to just assign the alert directly to the onclick, because in that case the alert statement is not assigned but executed.

The use of self-invoking functions might not be clear on first sight, but you might encounter them in e.g. jQuery libraries. So at the very least it will help you recognize the style:

( function() { alert("EXECUTE"); } )();

As you can see, the rather untransparent syntax is wrapping the function in parentheses and suffixing it with an additional ();When used, it will execute the contained function(s) without an explicit call in your HTML. In the example above, you might achieve the same result by just writing the alert statement without wrapping it in anything. But anonymous functions can also be used to limit the scope of certain variables to just the functions in the self-invoking wrapper. Many excellent posts were done on this subject, if you like to know more.

Javascript, unlike Java, with which it shares little in common but the name, has no support for overloading. Calling a function with less parameters than it has defined, will leave the additional parameters 'undefined':

function moo(cowName, cowColor) {
  console.log(cowName+", the "+cowColor+" cow, mooed.");
}
moo( "Robin", "red" ); // Robin, the red cow, mooed.
moo( "Uhura" ); // Uhura, the undefined cow, mooed.

As an alternative you can iterate over the 'arguments' object. It contains all the parameters passed to a function, even if you have not declared any parameters on the function definition:

function bark()
{
  var dogColor = "transparent";
  if( arguments.length == 2 ) {
    dogColor = arguments[1]; // only if 2 parameters were passed, overwrite the color with the supplied second parameter
  }
  console.log(arguments[0]+", the "+dogColor+" dog, barked.");
}
bark( "Bobby", "black" ); // Bobby, the black dog, barked.
bark( "Tiberius" ); // Tiberius, the transparent dog, barked.

Javascript variable declarations are normally prefixed with the keyword 'var'. When not prefixed with 'var', instead of throwing an error, the keyword will become global. And when left out unintentionally, this might lead to hard to find bugs:

function foo()
{
  GLOBAL_VAR = "Global";
  var localVar = "Local";
}
// GLOBAL_VAR is not yet a global variable
foo();
// Now GLOBAL_VAR is a global variable
log.console( "GLOBAL_VAR: "+GLOBAL_VAR );
log.console( "localVar: "+localVar ); // unknown variable error is thrown


object notation

To make code more readable, you can create objects in Javascript. For instance, we have several types of pollers, SimplePoller and AdvancedPoller. Both have a status. Without objects, you would need a function getSimplePollerStatus and a getAdvancedPollerStatus. That's not very readable. Also, defining objects will prevent function collision. In the previous example we know there are multiple pollers, but if we are working on a different source file and we don't know that one of the other source files also contains a function getStatus, we might define one ourselves. At runtime, the wrong function might be called. That would be function collision. Definining an object for the AdvancedPoller with its own child function getStatus would improve readability and prevent function collision:

function AdvancedPoller()
{
  this.status = 200;
  this.getStatus = function()
  { return this.status; }
}
var poller = new AdvancedPoller();
console.log(poller.getStatus());

To improve readability further Javascript functions can be grouped within namespaces. For a simple function that would like this:

var util = {
  foo : {
    bar : function()
    { console.log( "just ran util.foo.bar" ); }
  }
}
util.foo.bar();

For an object, the AdvancedPoller we defined earlier, it would look like this:

var poll =
{
  AdvancedPoller : function()
  {
    this.status = 200;
    this.getStatus = function()
    { return this.status; }
  }
}
var poller = new poll.AdvancedPoller();
console.log( poller.getStatus() );

Prototyping is a mechanism in Javascript to extend functionality on existing objects. In the following example I define a function seconds on the Javascript core object Number. It returns an amount of seconds (i.e. time since epoch + the amount of seconds) equal to the value of the Number object.

Number.prototype.seconds = function()
  { return new Date(this * 1000); }

And here I extend the Javascript core object Date with the function fromNow, that returns the current date increased by the date value in the Date object:

Date.prototype.fromNow = function()
{
  var x = new Date();
  x.setSeconds(x.getSeconds() + this.getSeconds() );
  return x;
}

This example makes a very readable function call, but at a pretty high price. Extending functionality on core objects in this way, may lead to function collision, because the Javascript core objects are not under your control. To extend objects in this way, especially core objects, you may want to wrap them in (jQuery) objects first.

So far the more curious, yet core functionality of Javascript. There are far more detailed guides on the web on the usage of these functions, and I left out some, but this is just meant as an introduction and to show that Javascript has grown up quite a bit in the past years.

You may also be interested in the related articles on html and css.