Archive for the ‘Javascript’ Category

Prototypal Inheritance in Javascript

When I decided to get my hands on Javascript, the biggest road block for me was to understand and follow the prototypal inheritance model in Javascript. Coming from a Ruby background, the inheritance model I learned and understood was the classical one. The prototypal model had fundamental differences and did run me nuts at times. Here I try to make some note of what its all about going along a few snippets.

What exactly is a prototype?
As explained in ‘Javascript: The Definitive Guide’ – An object’s prototype is a reference to another object from which properties are inherited. Every JavaScript object has a second JavaScript object (or null, but this is rare) associated with it. This second object is known as a prototype, and the first object inherits properties from the prototype.

In Javascript, an object can be created with object literals, with the new keyword, and (in ECMAScript 5) with the Object.create() function

  1. obj = { }                                                    //object literals, objects created will have Object.prototype as their prototype
  2. obj = Object.create(args, [args2])        //Object.create(), Objects created like this will have the first argument to the create method as their prototype
  3. obj = new Constructor invocation     //new Keyword, objects created this way will have the value of the prototype property of the constructor function as their prototype

Object.prototype: Its one of those rare objects which do not have a prototype, and hence is not associated with any other object.

Lets try and study the prototype of objects created via each of these 3 ways.
1) object literals{ }

obj = { }

obj will have always Object.prototype as its prototype. Inspecting obj in your console, and you will find this.

Prototype1

__proto__ is the prototype object ( Object.prototype) from which obj inherits the properties.

You can always use Object.getPrototypeOf to check the prototype of an object. The below screenshot shows the prototype object of obj.

prototype2

2) Object.create()

obj = Object.create(Object.prototype)

Doing this is exactly the same as creating an object with literals( obj = { }). This is because the first argument to the create method forms the prototype of the newly created object, obj. In our statement we have specified Object.prototype as the first argument to be used. Also note that Object.create also takes a second optional argument which defines the properties of the object.

We could specify an object or null as the first argument to the create method. If we pass null as the first argument to create, then the newly created object won’t inherit any property.

 Mammal = Object.create(null, {knownTime: { value: 'infinity' }} )

We have an object Mammal with no inherited properties. It has a property of its own, (knownTime with a value set to ‘infinity’).

Man = Object.create(Mammal)

Object.getPrototypeOf(Man) will return an object with a single property, knownTime. Also,

Man.knownTime
   -> 'infinity'

Now create another object inheriting from ‘Man’

John = Object.create(Man)
John.knownTime
   -> 'infinity'
 

Now, lets fiddle around these objects.
Try adding new properties to these objects.

Set a max height of Mammals.

Mammal.maxHeight = '200'
Man.maxHeight
   -> '200'
John.maxHeight
   -> '200'

Set a nice rhythmic vocal for mammals.

Mammal.sound = function() { return( "Wholaa!" ) }
Mammal.sound()
  -> "Wholaa!"
Man.sound()
  -> "Wholaa!"
John.sound()
  -> "Wholaa!"

Sure, Man has a different kind of a pitch from the other mammals.

Man.sound = function() { return( "Cheerpp!" )}
Man.sound()
   -> "Cheerpp!"
John.sound()
   -> "Cheerpp!"

Object.getPrototypeOf(John) returns an object(Man – with a property ‘sound’) which has another object(Mammal – with properties ‘knownTime, maxHeight and sound’) as its prototype.

prototype3

 

 

 

 

 

 

 

The prototype chain looks as follows

Prototype_Chain4 - New Page

Object Mammal has properties ‘knownTime’, ‘maxHeight’ and and the method ‘sound’.  Man has Mammal as its prototype object and hence inherits these properties from Mammal.  However, it overrides the ‘sound’ method. John has Man as its prototype object and hence inherits properties from Man(which includes Man’s own properties and properties inherited from Mammal).

3) new keyword followed by a Constructor Invocation

Try this,

obj = new Object()

Here again, this is exactly the same as Object.create(Object.prototype) and {}. The prototype of the object, obj is Object.prototype.

Here, Object is the constructor which when invoked using the new keyword will return a new object which inherits properties from the prototype of the constructor (here, Object.prototype).

Constructor(as per the Definitive Guide): A constructor is a function designed for the initialization of newly created objects. Constructors are invoked using the new keyword. Constructor invocations using new automatically create the new object, so the constructor itself only needs to initialize the state of that new object. The critical feature of constructor invocations is that the prototype property of the constructor is used as the prototype of the new object.

Also, note that every JavaScript function (except functions returned by the EC-MAScript 5 Function.bind() method) automatically has a prototype property. Hence, a constructor being merely a function also has the prototype property.
To make this more sense, lets create a constructor and try and figure out what exactly happens.

function Startup(config) {
   this.config = config ;
   this.motto = function(verbiage) {
      return("Our motto is" + verbiage)
    }
 }

Now, create a new instance

s1 = new Startup(true)

s1 is an object with Startup.prototype as its prototype, and hence inherits properties from Startup.prototype

s1.config
  -> true
s1.motto("we change")
  -> "Our motto is we change"

Create another instance,

s2 = new Startup(false)

s2 is an object with Startup.prototype as its prototype, and hence inherits properties from Startup.prototype

s2.config
  -> false
s2.motto("we build")
  -> "Our motto is we build!"

Now, we did like a bit more functionality to be achieved by all objects instantiated from Startup. For that to happen, we need to add more properties to Startup.prototype

Startup.prototype.space = function(people) { return(people/ 5) }
Startup.prototype.stream = function() {
   if(this.config == true)
       return("service") ;
   else
       return("business");
 }

Now, you could use these properties on the instances,

s1.space(25)
   -> 5
s1.stream()
   -> "service"

s2.space(40)
   -> 8
s2.stream()
   -> "business"

Prototoype_5 - New Page (1)

As is clear from the image above, we have 4 objects here, the constructor object(Startup) with its property(prototype), the prototype object (Startup.prototype) with all its properties and the instances(s1 & s2).

Now suppose we alter the prototype property of Startup

Startup.prototype = { stream : function() {return("No more a startup") }}

As is quite evident,

s1.stream()
   -> "No more a startup!"
s2.stream()
   -> "No more a startup!"

On this account, it might be clear on how to add new methods to be used by arrays, dates and other such objects created via a Constructor Invocation. With Array being the constructor function and Array.prototype being the prototype of all objects instantiated via the Array Constructor invocation,

A clone on all arrays,

Array.prototype.clone = function() {
   return this.concat()
   }

[1,2,3].clone()
   -> [1,2,3]

Clear off the array,

Array.prototype.clear = function() {
    this.length = 0 ;
    return this ;
   }

[1,2,3].clear()
    -> []
Advertisements