CodingBison

Most of the popular object-oriented programming languages offer a class-full object-oriented paradigm -- objects are based on a common template known as a class. Unlike these languages, JavaScript's object-oriented methodology is a class-free paradigm; JavaScript objects are not based on a common class, instead, they are based on yet another object, referred to as a Prototype.

Every JavaScript object has a prototype; the only exception to this rule is the global object, "Object". For this object, the prototype is NULL since it is the core prototype object around which all other objects are built.

For example, all JavaScript arrays have Array.prototype as their prototype, all JavaScript Strings have String.prototype as their prototype, all JavaScript Numbers have Number.prototype as their strings, all JavaScript Date objects have Date.prototype as their prototype and so on. Note that JavaScript Strings are objects that are wrapper for JavaScript's primitive string types and same is true for Number objects being a wrapper object around JavaScript's primitive number type.

Another important note is that all JavaScript objects have Object.prototype as their (ultimately) prototype -- directly or indirectly. The global object, "Object" inherits its properties from those of Object.prototype. Thus, if we were to add a property to Object.prototype (not a good idea, since it is the global object!), then Object would automatically inherit that property.

Now, let us understand what it means to have another object as an indirect prototype, using a simple example. JavaScript arrays have Array.prototype as prototype and then Array.prototype itself has Object.prototype as its own prototype. Thus, Object.prototype is an indirect prototype for all arrays.

If we need to test whether an object is a prototype of another object, then we can use JavaScript's isPrototypeOf() method and invoke it against the parent object and pass the child object as an argument to it. This method returns a boolean -- if the calling object is a prototype of the passed object, then this method returns true, else it returns false.

Let us see an example (provided below) that demonstrates the usage of the isPrototypeOf() method. This example also shows that Object.prototype is, in fact, the prototype of Array.prototype and of other objects as well.

 <!doctype html>
 <html>
 <div id="idDiv"></div>
 <script type="text/javascript"> 
 // Get a handle of the div element.
 var elem = document.getElementById("idDiv");

 // Define an array and test against its prototype.
 var a = ["Smaug", "Kilgharrah"];
 elem.innerHTML += Array.prototype.isPrototypeOf(a);                // Prints true

 // Define a function and test its against prototype.
 function getDragon () {return a[0]};
 elem.innerHTML += Function.prototype.isPrototypeOf(getDragon);     // Prints true

 // Test the array against the Function prototype.
 elem.innerHTML += Function.prototype.isPrototypeOf(a);             // Prints false

 // Test Object.prototype as a prototype. 
 elem.innerHTML += Object.prototype.isPrototypeOf(Array.prototype); // Prints true
 elem.innerHTML += Object.prototype.isPrototypeOf(a);               // Prints true
 elem.innerHTML += Object.prototype.isPrototypeOf(getDragon);       // Prints true

 // Test a sample object. 
 var obj = {name: "Smaug"}; 
 elem.innerHTML += Object.prototype.isPrototypeOf(obj);             // Prints true
 </script>
 </html>

As we discussed earlier, JavaScript objects have a "prototype" link that points to their prototype. Due to this, the relationship between a prototype and its objects is fairly unique. Methods present with prototype become automatically available to all of its objects. Also, due to the linkage, if we add a method to the prototype, then the newly-added method becomes available to all the objects based on that prototype, including those objects that were defined prior to adding the method.

Let us see this behavior using a simple example (shown below). Here, we start by defining an array, "a" and provide some initial values to it. Next, we define a function getLastElement() which returns the last value of the current array object; note that this function uses the "this" keyword to access the current object in scope, that in this case is the array object being passed to this function. After that, we simply add this function as a new method (returnLastElement) to the Array.prototype: "Array.prototype.returnLastElement = getLastElement;". With this, the newly added method becomes available to "a" array even though it was defined before we added the method to Array.prototype.

 <!doctype html>
 <html>
 <div id="idDiv"></div>

 <script type="text/javascript"> 
 // Get a handle of the div element.
 var elem = document.getElementById("idDiv");

 // Function to return the Last element. Here, "this" refers 
 // to the current object (in this case, array) in scope.
 function getLastElement () {return this[this.length-1];}

 // Define an array to hold names of some of the "famous" dragons.
 var a = ["Kilgharrah", "Chrysophylax", "Vermithrax Pejorative"];

 // Assign getLastElement to Array.prototype. 
 Array.prototype.returnLastElement = getLastElement;

 // Invoke the new method from an array. 
 elem.innerHTML += a.returnLastElement();    // Prints "Vermithrax Pejorative"

 </script>
 </html>

However, we should note that for objects based on a prototype, modifying the value of the object does not affect the parent prototype object.

Calling Prototype Methods

Objects based on a prototype can automatically access methods provided by its prototype. However, there are cases when both the child object and its prototype define the same method and in such cases, the child's method shadow's the prototype's method. Thus, invoking the method on the child object invokes the method defined by the child object and not the one defined by its prototype.

However, for some use-cases, we might need to invoke the prototype's method from the child context and skip invoking the child's method. For such cases, we need to explicitly invoke the method defined by the prototype.

Let us use a simple example that invokes the same method that is defined by both the child object (Array.prototype) and its prototype (Object.prototype). Since arrays inherit the methods defined by Array.prototype, we use an array to invoke methods defined by Array.prototype. Array.prototype overwrites some of the methods defined by its prototype (Object.prototype) like toString() and valueOf(). This example uses these two methods and invokes them from both contexts. It is important to note that the examples uses the call() function to invoke the method defined by Object.prototype; we will revisit the call() function in a later section on JavaScript functions.

 <!doctype html>
 <html>
 <div id="idDiv"></div>

 <script type="text/javascript">
 // Get a handle of the div element.
 var elem = document.getElementById("idDiv");

 // Define an array. 
 var a = ["Kilgharrah"];

 // Invoke toString() method defined by the child object. 
 elem.innerHTML += a.toString();                      // Prints "Kilgharrah"

 // Invoke valueOf() method defined by the child object. 
 elem.innerHTML += a.valueOf();                       // Prints "Kilgharrah"


 // Invoke toString() method defined by the prototype. 
 elem.innerHTML += Object.prototype.toString.call(a); // Prints "[object Array]"

 // Invoke valueOf() method defined by the prototype. 
 elem.innerHTML += Object.prototype.valueOf.call(a);  // Prints "Kilgharrah"

 </script>
 </html>

The above example shows an interesting behavior of toString() method. When invoked in the context of the child object (here Array.prototype), it prints the value of the array converted to string. When invoked in the context of the prototype object (Object.prototype), it prints the class-definition of the child object. On the other hand, the method valueOf() yields same result for both invocations.





comments powered by Disqus