Friday, September 26, 2008

Five Things You Didn't Know JavaScript Could Do

The Stretch Mark of the Internet

Depending on who you talk to, JavaScript is probably held in a very different light. On one hand it's responsible for a lot of the browser exploits and popups that make the web irritate like a rash. It's also a language that didn't always have great IDE and debugging support which made authoring anything of size in JavaScript more than a headache. On the other it's really the only language you can use to write client side web applications (less Flex/Silverlight).

As the web continues to change, JavaScript is starting to play a more important role. More and more JavaScript is starting to be viewed as the duck tape of the Internet. This funky interperated language is slowly getting street credit for having a very expressive syntax that when combined with a good client side API, can give developers what they need to build highly interactive sites. Without further ado, here's some cool JavaScript features, some of these are only just now available to .NET v3.0/v3.5 languages (like lambda expressions and extension methods).

Object, A Dictionary

JavaScript has a similar inheritance hierarchy to Java where almost everything is an object or extends object. Object itself (the class) is actually a dictionary like data structure. All the codes below are all equivalent.

var userObject = new Object();
userObject.lastLoginTime = new Date();
alert(userObject.lastLoginTime);
var userObject = {}; // equivalent to new Object()
userObject[“lastLoginTime”] = new Date();
alert(userObject[“lastLoginTime”]);
var userObject = { “lastLoginTime”: new Date() };
alert(userObject.lastLoginTime);

The last one is a lot like C# v3.0 object initializers.

Functions as Objects

Functions are also objects, you can assign them to variables, pass them into other functions or put them into other data structures (ie. an array of functions).

var addFunction = function(x, y)
{
return x+y;
};

//This is valid;
alert(addFunction(5,3));

function alertOutput(functionDef, input1, input2)
{
alert(functionDef(input1, input2));
}

alertOutput(addFunction, 5, 3);

Adding Functions and The Prototype Chain

Because JavaScript allows you to assign properties and methods at runtime, you can selectively grab an instance of an object and then add functionality to it. Consider the following code which adds an addDays() function to a single instance of a date object.

var date = new Date();
date.addDays = function(days)
{
this.setDate(Number(this.getDate()+days));
}
date.addDays(5); //works!

var date2 = new Date();
date2.addDays(5); //throws error!

But we can also add functionality to ALL JavaScript objects by extending the prototype chain. All objects in JavaScript have a root blueprint or original class which is dubbed the prototype. By altering the prototype ALL instances of that class will get the new functionality.

Date.prototype.addDays = function(days)
{
this.setDate(Number(this.getDate()+days));
}

var date = new Date();
date.addDays(5); //works!!
var date2 = new Date();
date2.addDays(10); //also works!!

In the above example we give all Date objects (existing and future) additional functionality (the method addDays). Notice how similar this is to extension methods (except better cause it's not only static).

Closures

Closures allow you to create functions that build out expressions (read functions) programmatically. These expression can then be attached to an object, used to extend the prototype chain etc... Here's an example of a closure:

//Takes a function that filters numbers and calls the function on
//it to build up a list of numbers that satisfy the function.
function filter(filterFunction, numbers)
{
var filteredNumbers = [];

for (var index = 0; index < numbers.length; index++)
{
if (filterFunction(numbers[index]) == true)
{
filteredNumbers.push(numbers[index]);
}
}
return filteredNumbers;
}

//Creates a function (closure) that remembers the value "lowerBound"
//that gets passed in and keep a copy of it.
function buildGreaterThanFunction(lowerBound)
{
return function (numberToCheck) {
return (numberToCheck > lowerBound) ? true : false;
};
}

var numbers = [1, 15, 20, 4, 11, 9, 77, 102, 6];

var greaterThan7 = buildGreaterThanFunction(7);
var greaterThan15 = buildGreaterThanFunction(15);

numbers = filter(greaterThan7, numbers);
alert('Greater Than 7: ' + numbers);

numbers = filter(greaterThan15, numbers);
alert('Greater Than 15: ' + numbers);

This functionality bears a striking resemblance to Lamba expressions.

Inheritance

There's probably no real surprise here, but JavaScript can also make use of inheritance. You can extend classes and overwrite behaviors down the inheritance chain (polymorphism). You can also make methods private and hide the details of classes (encapsulation).

// Defines a Pet class constructor
function Pet(name)
{
this.getName = function() { return name; };
this.setName = function(newName) { name = newName; };
}

// Adds the Pet.toString() function for all Pet objects
Pet.prototype.toString = function()
{
return 'This pets name is: ' + this.getName();
};
// end of class Pet

// Define Dog class constructor (Dog : Pet)
function Dog(name, breed)
{
// think Dog : base(name)
Pet.call(this, name);
this.getBreed = function() { return breed; };
}

// this makes Dog.prototype inherit from Pet.prototype
Dog.prototype = new Pet();

// Currently Pet.prototype.constructor
// points to Pet. We want our Dog instances'
// constructor to point to Dog.
Dog.prototype.constructor = Dog;

// Now we override Pet.prototype.toString
Dog.prototype.toString = function()
{
return 'This dogs name is: ' + this.getName() +
', and its breed is: ' + this.getBreed();
};
// end of class Dog

var parrotty = new Pet('Parrotty the Parrot');
var dog = new Dog('Buddy', 'Great Dane');
// test the new toString()
alert(parrotty);
alert(dog);

// Testing instanceof (similar to the `is` operator)
alert('Is dog instance of Dog? ' + (dog instanceof Dog)); //true
alert('Is dog instance of Pet? ' + (dog instanceof Pet)); //true
alert('Is dob instance of Object? '+(dog instanceof Object)); //true

Another Chance

Whether you like it or not, JavaScript may be your most important tool when it comes to client side web development these days. It plays a huge role and still doesn't really have a comparable alternative. Not only does the language deserve your attention, but becoming deep in this scripting language will open up huge doors when it comes to writing interactive web applications.

My Best,
Tyler

6 comments:

Anonymous said...

I... I think most people know this stuff now.

Jason Whaley said...

@anonymous
Not really... I was at a developer's conference last March and attended a Javascript session. When the speaker asked for a show of hands of the number of people who knew and/or worked with javascript regularly, only about 1/5 of the audience raised their hands. I was not one of them.

Informative post, I'd say. Thanks!

Tyler Holmes said...

I'll fess up that before I wrote this I wasn't particularly deep with some of these features. I'm positive there's a ton of developers out there that can run circles around me when it comes to JavaScript, but I still feel that topics like extending the prototype chain and making use of and closures aren't particularly well known or leveraged. Either way thanks for the feedback!

Paul said...

Another thing you could add to functions being objects, you can do this (blow's my mind every time I use it):

var funct = function() {
  alert('Main Function');
};
funct.subfunct = function() {
  alert('Sub Function');
};
funct.both = function() {
  this();
  this.subfunct();
};

// This is how you'd call them
funct();
funct.subfunct();
funct.both();

Tyler Holmes said...

Nice moves Paul, maybe we should call it "Six Things You Didn't Know JavaScript could do". Thanks for sharing.

Dylan said...

Tyler,

So I StumbledUpon this article and began reading it and being pleasantly surprised about how clearly you were able to explain the JavaScript prototype model. However, what really surprised me was your name and photo in the right side of the page. "Tyler Holmes", could it be the same Tyler that I worked with in Beaverton, the same Tyler who I immensely respect as a programmer and as a person. I do believe it is. It's been a while buddy, how are you. In case you haven't read my name yet, it's Dylan from B&J.

I'm living in Indiana now.

What's new with you?

P.S. Great article by the way.