Properties, methods, and the elusive this oh my what’s a developer to do? Read this guide to writing elegant object oriented (OO) JavaScript I respond!
Learning to write elegant JavaScript is important to your career as a developer. With the advent of technologies such as Node.js you can now write JavaScript on the server side, likewise you can even use JavaScript to query a persistent data store like MongoDB.
So let’s get started writing some OO JS. If you have any questions or I missed something please leave me a message in the comments.
Literal notation is just one way of creating an object in JavaScript, yes there’s more than one. Literal notation is the preferred method when you only plan on creating one instance of an object.
var bill = {};
The above code isn’t very useful, it’s just an empty object. Let’s dynamically add some properties and methods to this object.
bill.name = "Bill E Goat";
bill.sound = function() {
console.log( 'bahhh!' );
};
Here we add the property “name” and assigned it the value “Bill E Goat”. We also gave bill our billy goat the ability bahhh. We didn’t have to create an empty object first though, we could have done it all in one fell swoop with literal notation.
var bill = {
name: "Bill E Goat",
sound: function() {
console.log( 'bahhh!' );
}
};
Beautiful isn’t it? Accessing properties and methods is a breeze as well.
bill.name; // "Bill E Goat"
bill.sound(); // "bahhh"
If our property name isn’t a valid identifier we can also access it like this.
bill['name']; // "Bill E Goat"
Notice that when calling a method we append parenthesis to the method name to call it. Let’s override our current sound method and pass in a parameter in the process.
bill.sound = function(noise) {
console.log( noise);
};
bill.sound("brrr!"); // "brrr!" He's cold :)
Awesome, we passed in a parameter (noise), and accessed it from inside our function definition. Now we’re rolling, next let’s add an additional method to our object that accesses the name property.
bill.sayName = function() {
console.log( "Hello " + this.name );
};
bill.sayName(); // "Hello Bill E Goat"
We can access properties from inside a method using the notation this.propertyName so in our case this.name.
bill.sayName; // function
Whoa what happened there. We called the sayName method w/ out the parenthesis and it returned a function defintion. Let’s dig digger.
var sound = bill.sound;
sound('moo!'); // "moo!"
Well that’s pretty sweet, we assigned our objects sound method definition to a local function named sound. We can than call that function by applying parentheses and passing a parameter. What do you think happens when we try cloning Bill (remember how things worked out for poor Dolly)?
var sally = bill;
sally.name; // "Bill E Goat", But her name is Sally silly
sally.name = "Sally";
sally.name; // "Sally", Better
bill.name; // "Sally", Oh no what happened to Bill
In the example above we create a new variable sally, and make it equal to bill. Both sally and bill now reference the same object in memory. Changes made via one affect both of them.
Rest assured though a solution to our problem exists w/ Constructor Notation. Before digging into Constructor Notation though let’s look at one more potential gotcha w/ Object Literals.
bill.name = "Bill E Goat";
bill.sayName(); // "Hello Bill E Goat";
var sayName = bill.sayName;
sayName; // function, OK so far so good
sayName(); // "Hello ", huh why didn't it print out Bills name?
Bill’s name is a local instance variable, as such it’s only available to well, Bill. Specifically the sayName function was created in the global scope, so when it came across the statement this.name it looked in the global scope for the value of name. The only problem is name isn’t defined at this point so we’re left w/ “Hello “. Let’s define name in the global scope and watch what happens.
var name = "Bearded Octo";
sayName(); // "Hello Bearded Octo"
Here we create a name variable in the global scope, and assign it the value “Bearded Octo”. When we call our sayName function it looks in the global scope for name and accesses the value “Bearded Octo”. Beautiful. The problem remains though. How can we create a ‘class’ upon which to base the construction of multiple objects?
Constructor notation is another of way writing OO JavaScript. Constructor notation is preferred when you need to set initial properties, and methods on an object or you plan on creating multiple instances of an object where the properties, and methods of each instance need to be unique. Let’s create an empty object to get us started.
function Game() {};
Notice the convention of capitalizing the first letter to distinguish it as a class. We can use this class to build new objects.
var zelda = new Game();
var smb = new Game();
zelda.title = "Legend of Zelda";
smb.title = "Super Mario Brothers";
zelda.title; // "Legend of Zelda"
smb.title; // "Super Mario Brothers"
Our objects maintain their own properties now! When creating objects we can even pass in properties, and change those properties later.
function Game(title) {
this.title = typeof title !== 'undefined' ? title : "";
};
var zelda = new Game("Legend of Zelda");
zelda.title; // "Legend of Zelda"
zelda.title = "Ocarina of Time";
zelda.title; // "Ocarina of Time"
var blank = new Game();
blank.title; // ""
The second line might be confusing. We’re using the ternary operator which is just a way of putting an if else block on one line. It’s equivalent in a standard if else block is noted below.
if (typeof title !== 'undefined') {
this.title = title;
} else {
this.title = "";
}
// Is the same as
this.title = typeof title !== 'undefined' ? title : "";
If a title parameter is passed in when the object is created, the title instance variable is assigned the value of title. If no title is passed in the title instance variable is assigned a default value of “”.
We can create a method to access this property as well.
zelda.loveTitle = function() {
console.log( "I love " + this.title );
};
zelda.loveTitle(); // "I love Ocarina of Time"
Well that’s kind of neat, but we can do better. We can add a method to the Game class that will be available to all objects created from the Game class.
Game.prototype.heartIt = function() {
console.log( "I heart " + this.title );
};
zelda.heartIt(); // "I heart Ocarina of Time"
smb.heartIt(); // "I heart Super Mario Brothers"
I hope this tutorial proved useful in introducing you to OO JS. There are still many aspects of OO JS to cover, which I hope to go over in a future tutorial. If there is anything in particular you’d like me to cover please note it in the comments. Thank you for reading :).
Great quick intro