Javascript Basics Part 8
new Object and Object Literals
To begin with we need to explain how Objects work in Javascript. Objects allow you to define a variable and then set any number of properties on that variable. To understand this, let's take a look at a simple example:
var myObj = new Object; myObj.a = 5; myObj['b'] = 10; myObj.c = 20; myObj.getTotal = function(){ alert(this.a+this.b+this.c); }); // or var myObj = {a:5, b:10, c:20, getTotal:function(){ alert(this.a+this.b+this.c); }};Both of these snippets create an identical variable, myObj. Our first example uses the new Object() syntax and then defines each property one by one. The 2nd snippet is short-hand notation that does exactly the same thing. We now have a variable, myObj. myObj contains 3 integer variables: a, b and c. To access any of these we simply use myObj.a or myObj['a']. You'll notice that myObj also has a function as a property, getTotal. We access getTotal the same way we access our a, b and c properties: myObj.getTotal(). You'll notice that our getTotal function accesses variables in the myObj variable by using "this". When you are running code from within a function in an object, "this" refers to the object. In this case "this" refers to myObj itself. As you can see, objects are incredibly useful in JavaScript. Unfortunately, this is often far too much information to declare each time we want a new object. For example, let's write an Animal Object:
var myAnimal = { name: 'felix', species: 'cat', talk: function(){ alert('Meow!'); }, callOver: function(){ alert(this.name+' ignores you'); }, pet: function(){ alert('Purr!'); } }We now have a variable, myAnimal, that represents a cat named felix. Unfortunately if we want to create another cat, we need to type all of this again. This is where Object-Oriented design comes in. Instead of re-typing the entire object each time, we can create a function that creates a similar object for us:
function Cat(name){ this.name = name; this.species = 'Cat'; this.talk = function(){ alert('Meow!'); } this.callOver = function(){ alert(this.name+' ignores you'); }, this.pet = function(){ alert('Purr!'); } } var felix = new Cat('Felix'); var sam = new Cat('Sam'); var patty = new Cat('Patty'); felix.pet(); // alerts 'Purr!' sam.callOver(); // alerts 'Sam ignores you'. Just like a cat! alert(patty.species); // alerts 'cat'

Prototyping
We can re-write our Cat function so that each function is declared only once:
function Cat(name){ this.name = name; } Cat.prototype.species = 'Cat'; Cat.prototype.talk = function(){ alert('Meow!'); }; Cat.prototype.callOver = function(){ alert(this.name+' ignores you'); }; Cat.prototype.pet = function(){ alert('Purr!'); };

Cat.prototype.sleep = function(){ alert(this.name+' falls asleep'); };Not only is this quicker, but we no longer have to keep track of every cat in order to add the sleep function. There is a quicker way to add prototypes. By using the object literal that we saw earlier in the article, we can set as many prototype properties or methods as we want at the same time.
function Cat(name){ this.name = name; } Cat.prototype = { species: 'Cat', talk: function(){ alert('Meow!'); }, callOver: function(){ alert(this.name+' ignores you'); }, pet: function(){ alert('Pet!'); } }It is important to note that we can only set prototypes on a function one time using this method. After that, you must use the previous method, Object.prototype.method = function(){ ... }. If we were to now try and add a sleep method to our cats using this method,
Cat.prototype = { sleep: function(){ alert(this.name+' falls asleep'); } }our previous prototypes, species, talk, callOver and pet, would all be erased. The only prototype we would now have on our Cats would be sleep. Prototypes can also be used to extend JavaScript's built-in objects. We can implement a String.prototype.reverse function, for instance, that will return the reverse of any string we create:
String.prototype.reverse = function(){ var out = ''; for(var i=this.length-1; i>=0; i--){ out+=this.substr(i, 1); } return out; } alert('asdf'.reverse());This can be a very useful tool when used correctly. You can implement String.prototype.trim() to trim any whitespace off of a string, Date.prototype.dayName to return the day name from a Date object, etc. It is strongly suggested that you refrain from adding any prototypes to Array or Object, however, as doing this breaks "for-in" loops for these two data types. Consider the following example:
var myArray = [1, 2, 3]; for(n in myArray) alert(n); // alerts 0, 1 and 2 - the indexes of the array. Array.prototype.something = function(){ }; for(n in myArray) alert(n); // alerts 'something', 0, 1 and 2.As you can see, we prototyped Array and added a 'something' function. Now, however, that 'something' function is visible as an element of the array, a result that we did definitely not expect and do not want. The same thing occurs with Objects and Object Literals if you prototype Object. If you are 100% certain that you will never use a "for-in" loop and are certain that no other developer will be using your JavaScript code, then feel free to prototype Array or Object, but be aware of the issues with it. There are other methods of achieving the same results, however. My personal preference is to extend Array without using prototype:
Array.find = function(ary, element){ for(var i=0; i<ary.length; i++){ if(ary[i] == element){ return i; } } return -1; } alert(Array.find(['a', 'b', 'c', 'd', 'e'], 'b')); // alerts 1As you can see, we now have to type Array.find(ary, e) instead of ary.find(e) as we would if we prototyped the Array Object, but these extra few characters are worth typing in order to not break existing JavaScript functionality.
Private, Public and Static variables
The way that we define variables in our Objects determines what methods our Objects have available to access those variables. In JavaScript, there are five levels of methods and properties when working with OO code.
Private | Declared with 'var variableName' or 'function functionName' inside of the object. Can only be accessed by other private or privileged functions. |
Public | Declared with 'this.variableName' inside of the object. Can be changed by any function or method. |
Privileged | Declared with 'this.functionName = function(){ ... }' inside of the object. Can be accessed by any function or method and can call reference or change any Private variable. |
Prototype | Declare with 'Class.prototype.variableName' or 'Class.prototype.functionName'. Functions declared this way will have access to any public or prototype variables. Attempts to change variable created this way will instead create a new public variable on the object and the prototype variable will be unavailable. |
Static | Declare with 'Class.variableName' or 'Class.functionName'. Can be changed by any function or method. This method is rarely used. |
function Cat(name, color){ /* Constructor: any code in here is run when the object is created */ Cat.cats++; /* Private variables and functions - may only be accessed by private or privileged functions. Note that 'name' and 'color', passed into the Class, are already private variables. */ var age = 0; var legs = 4; function growOlder(){ age++; } /* Public variables - may be accessed publicly or privately */ this.weight = 1; this.length = 5; /* Privileged functions - may be accessed publicly or privately May access Private variables. Can NOT be changed, only replaced with public versions */ this.age = function(){ if(age==0) this.length+=20; growOlder(); this.weight++; } } /* Prototyped Functions - may be accessed publicly */ Cat.prototype = { talk: function(){ alert('Meow!'); }, callOver: function(){ alert(this.name+' ignores you'); }, pet: function(){ alert('Pet!'); } } /* Prototyped Variables - may be accessed publicly. May not be overridden, only replaced with a public version */ Cat.prototype.species = 'Cat'; /* Static variables and functions - may be accessed publicly */ Cat.cats = 0;As you can see, there are many levels of permissions to consider. As we discussed earlier, all Private, Privileged and Public functions and variables are copied each time you make a new instance of your Object. In general, nearly everything you need to do can be accomplished with prototypes and public variables. For these two reasons, and because of a concept called Closures, it is generally a good idea to refrain from using Private, Privileged and Public variables and functions unless you have a specific need to do so. That's all for this article. In our next article we will continue with Object Oriented code and discuss Closures and Class inheritence.