mcgarrybowen, New York
- 601 West 26th Street Suite 1150
- New York, New York 10001
- United States
- Phone: 212 598 2900
- Fax: 212 598 2996
- Country Phone Code: 1
- Email: info@mcgarrybowen.com
Object-Oriented JS Demystified
October 4, 2012
First, a little background:
A good way to throw off a front-end programmer of any level is to ask them to talk about their experience with Object Oriented Javascript. This is a powerful question because your typical front-end programmer can be put into one of a few groups, each of which has a legitimate reason to not feel so comfortable with OO JS:
A beginner front-end programmer:
Front-end programming is a very special kind of programming. It doesn’t require a strong knowledge of computer science and knowing another language won’t help you learn HTML; it’s a beast of its own. Because of this, a beginner programmer is likely not familiar with Object Oriented programming at all, let alone how to accomplish it with Javascript.
A seasoned OO programmer in a language other than Javascript:
This group makes up the majority of the “I hate Javascript” posts on the web. Javascript is a special language (in every sense of the word ‘special’). There are countless blog posts dedicated to Javascript’s quirks – my opinion is that while these quirks do allow for some horribly difficult bugs, they also allow for some very powerful development techniques when used correctly. Programmer’s comfortable with traditional OO Languages will find the syntax below confusing, silly and riddled with potential issues if the code isn’t executed perfectly.
An experienced Object Oriented Javascript programmer
My heart goes out to this group. The syntax for object oriented javascript, as you will see, is whacky. As opposed to nearly every other language, where there is a single way to define an object and a single way to define what that object can do, with javascript, there are countless ways to do it. This leaves even the experienced programmers uneasy as to whether they are doing it “correctly.”
The basics:
This is how you create a new, empty, class called “Person.” In every other language the word “class” would appear somewhere in the code. With javascript, it doesn’t. Get over it.
var Person = function(){ }; |
Let’s make this a more complete example by adding “instance methods” and “instance variables.” Instance methods define everything a “Person” can do or have done to them. Instance variables are properties a Person can have. Once these are defined, we’ll create our first instance of Person and call our instance method.
var Person = function(config){ //this can be thought of as your constructor, //config being the data passed into this instance. this.name = config.name; //this instance how has a propery called "name" //set to the name attribute of our passed config. }; //This is the whacky way we add instance methods. Person.prototype.sayHello = function(){ alert("Hello, my name is " + this.name); }; //This is the whacky way we add public static class methods. Person.get = function(name){ //find a person. }; //Create an instance var greg = new Person({ name : 'Greg'}); greg.sayHello(); //"Hello, my name is Greg" |
A proposed cleaner solution
Personally, I’ve never liked the idea of having my instance methods being defined outside of my class. In every other language, you define a class as a self contained entity. The following solution is identical but has a few huge advantages.
- Person is all packaged up with a private scope
- If for some reason you decide you need to change the name of the class, it can be done in one place without effecting the inner workings of the class
var Person = (function(){ /* Because of scope, this area is safe to define variables without fear of being overwritten or overwriting another poorly-scoped variable. A variable defined here can be thought of as a "private static class variable." This means that only instances of Person can access this variable, and if one instance updates it, it affects all other instances. */ //as an example, we'll store all instances created //created with this "private static class" variable var allPeople = []; //This is the "class" you are going to return, once you have assigned all instance methods. //This class has access to the above protected scope. var _Person = function(config){ this.name = config.name; this.num = allPeople.length + 1; allPeople.push(this); }; _Person.prototype.sayHello = function(){ //Notice this.name / this.num versus allPeople. alert("Hello, my name is " + this.name + ". I am person #" + this.num + ' of ' + allPeople.length + ' total people'); }; _Person.get = function(who){ //clearly an oversimplified example assuming there's only one user per name //and there aren't so many users where looping through is too expensive. for(var i = 0; i < allPeople.length; i++){ if (allPeople[i].name == who) return allPeople[i]; } } return _Person; })(); var greg = new Person({name : 'Greg'}); var dan = new Person({name : 'Dan'}); greg.sayHello(); //Hello, my name is Greg. I am person #1 of 2 total people Person.get("Dan").sayHello(); //Hello, my name is Dan. I am person #2 of 2 total people alert(typeof allPeople) //undefined, because we are not inside the class. This is good. |
So what?
This is a very valid reaction to all of this. The reality is that 95% of web development right now doesn’t benefit from this because of the simplicity of your typical website. Does it make sense to create a complicated object just to attach a click event listener? Absolutely not. But the moment your website pushes its way into a web application, abstracting your core functionality into re-usable, inheritable objects is an absolute must.