Prototype Pattern

Description

Each function is created with a prototype property, which is an object containing properties and methods.

The benefit of using the prototype is that all of its properties and methods are shared among object instances.

Example


function Person(){//from   ww  w.  j av  a  2 s .  com
}
Person.prototype.name = "XML";
Person.prototype.age = 29;
Person.prototype.job = "Mark up";
Person.prototype.sayName = function(){
   console.log(this.name);
};

var person1 = new Person();
person1.sayName();   //"XML"

var person2 = new Person();
person2.sayName();   //"XML"

console.log(person1.sayName == person2.sayName);  //true

The code above generates the following result.

Note

The properties and methods are all shared among instances, so person1 and person2 are both accessing the same set of properties and the same sayName() function.

It's possible to read values on the prototype from object instances, it is not possible to overwrite them.

If you add a property to an instance that has the same name as a property on the prototype, you create the property on the instance, which then masks the property on the prototype.


function Person(){//  ww  w  . j  a v a 2  s .  c o  m
}
Person.prototype.name = "XML";

var person1 = new Person();
var person2 = new Person();

person1.name = "CSS";
console.log(person1.name);   //"CSS" - from instance
console.log(person2.name);   //"XML" - from prototype

The code above generates the following result.

Delete property

The delete operator removes the instance property and allows the prototype property to be accessed again.


function Person(){/*w w  w  . j a v a2  s .  co m*/
}

Person.prototype.name = "XML";

var person1 = new Person();
var person2 = new Person();
person1.name = "CSS";
console.log(person1.name);   //"CSS" - from instance
console.log(person2.name);   //"XML" - from prototype

delete person1.name;
console.log(person1.name);   //"XML" - from the prototype

The code above generates the following result.

Has property

The hasOwnProperty() method inherited from Object determines if a property exists on the instance or on the prototype.

It returns true only if a property of the given name exists on the object instance.


function Person(){/* w w w  .  java2 s . c  o  m*/
}

Person.prototype.name = "XML";

var person1 = new Person();
var person2 = new Person();

console.log(person1.hasOwnProperty("name"));  //false

person1.name = "CSS";
console.log(person1.name);   //"CSS" - from instance
console.log(person1.hasOwnProperty("name"));  //true

console.log(person2.name);   //"XML" - from prototype
console.log(person2.hasOwnProperty("name"));  //false

delete person1.name;
console.log(person1.name);   //"XML" - from the prototype
console.log(person1.hasOwnProperty("name"));  //false

The code above generates the following result.

Prototypes and the in Operator

There are two ways to use the in operator: on its own or as a for-in loop.

When used on its own, the in operator returns true if the property may exist on the instance or on the prototype.


function Person(){/*from www . j  a  va2s . c  om*/
}
Person.prototype.name = "XML";

var person1 = new Person();
var person2 = new Person();

console.log(person1.hasOwnProperty("name"));  //false
console.log("name" in person1);  //true

person1.name = "CSS";
console.log(person1.name);   //"CSS" - from instance
console.log(person1.hasOwnProperty("name"));  //true
console.log("name" in person1);  //true

console.log(person2.name);   //"XML" - from prototype
console.log(person2.hasOwnProperty("name"));  //false
console.log("name" in person2);  //true

delete person1.name;
console.log(person1.name);   //"XML" - from the prototype
console.log(person1.hasOwnProperty("name"));  //false
console.log("name" in person1);  //true

The code above generates the following result.

Property existance

It's possible to determine if the property of an object exists on the prototype by combining a call to hasOwnProperty() with the in operator like this:


function hasPrototypeProperty(object, name){
   return !object.hasOwnProperty(name) && (name in object);
}

Example


function hasPrototypeProperty(object, name){
   return !object.hasOwnProperty(name) && (name in object);
}// w  w w  . j a va  2 s.co  m

function Person(){
}
Person.prototype.name = "XML";

var person = new Person();
console.log(hasPrototypeProperty(person, "name"));  //true
person.name = "CSS";
console.log(hasPrototypeProperty(person, "name"));  //false

The code above generates the following result.

for each property

When using a for-in loop, all properties that are accessible by the object and can be enumerated will be returned, which includes properties both on the instance and on the prototype.

Instance properties that shadow a non-enumerable prototype property (a property that has Enumerable set to false) will be returned in the for-in loop.


var o = {
   toString : function(){//from w w  w .j av  a  2 s .c o  m
     return "My Object";
   }
};

for (var prop in o){
   if (prop == "toString"){
      console.log("Found toString"); 
   }
}

The object o has an instance property called toString() that shadows the prototype's toString() method which is not enumerable.

The code above generates the following result.

Object.keys()

To retrieve a list of all enumerable instance properties on an object, you can use the Object.keys() method.

It accepts an object as its argument and returns an array of strings containing the names of all enumerable properties.


function Person(){//w  ww . j  a  va2s . c  o m
}
Person.prototype.name = "XML";
Person.prototype.age = 29;
Person.prototype.job = "Markup";
Person.prototype.sayName = function(){
   console.log(this.name);
};

var keys = Object.keys(Person.prototype);
console.log(keys);       //"name,age,job,sayName"

var p1 = new Person();
p1.name = "CSS";
p1.age = 31;
var p1keys = Object.keys(p1);
console.log(p1keys);    //"name,age"

The code above generates the following result.

Get Own Property Names

To list all instance properties enumerable or not, you can use Object.getOwnPropertyNames().


function Person(){/*from   w w  w.  j  a v  a 2s .  co  m*/
}
Person.prototype.name = "XML";
Person.prototype.age = 29;
Person.prototype.job = "Markup";
Person.prototype.sayName = function(){
   console.log(this.name);
};
var keys = Object.getOwnPropertyNames(Person.prototype);
console.log(keys);  

It includes the non-enumerable constructor property in the results.

Both Object.keys() and Object.getOwnPropertyNames() may be suitable replacements for using for-in.

The code above generates the following result.

Alternate Prototype Syntax

We don't need to type prototype for each property and method.


function Person(){/*  ww  w. j  ava 2  s  .  c  o  m*/
}
Person.prototype = {
    name : "XML",
    age : 29,
    job : "Mark up",
    sayName : function () {
        console.log(this.name);
    }
};
var friend = new Person();
console.log(friend instanceof Object);      //true
console.log(friend instanceof Person);      //true
console.log(friend.constructor == Person);  //false
console.log(friend.constructor == Object);  //true

In the code above the constructor property no longer points to Person.

To set constructor's back to the appropriate value.


function Person(){/*from   w w  w.  ja  v  a2 s.c o  m*/
}

Person.prototype = {
   constructor: Person,
   name : "XML",
   age : 29,
   job : "Mark up",
   sayName : function () {
       console.log(this.name);
   }
};
var friend = new Person();
console.log(friend instanceof Object);      //true
console.log(friend instanceof Person);      //true
console.log(friend.constructor == Person);  //true
console.log(friend.constructor == Object);  //true

To make constructor properties not-enumerable, use Object.defineProperty().


function Person(){// ww  w.j  a  va  2  s .c  o m
}

Person.prototype = {
   name : "XML",
   age : 29,
   job : "Mark up",
   sayName : function () {
      console.log(this.name);
   }
};
//ECMAScript 5 only - restore the constructor
Object.defineProperty(Person.prototype, "constructor", {
    enumerable: false,
    value: Person
});
var friend = new Person();
console.log(friend instanceof Object);      
console.log(friend instanceof Person);      
console.log(friend.constructor == Person);  
console.log(friend.constructor == Object);  

Dynamic Nature of Prototypes

Changes made to the prototype at any point are immediately reflected on instances.


function Person(){//from  ww  w .  j  a v a 2  s  .  c  o  m
}

Person.prototype = {
   name : "XML",
   age : 29,
   job : "Mark up",
   sayName : function () {
      console.log(this.name);
   }
};

Person.prototype.sayHi = function(){
   console.log("hi");
};

var friend= new Person();
friend.sayHi();   //"hi" - works!

The code above generates the following result.





















Home »
  Javascript »
    Javascript Introduction »




Script Element
Syntax
Data Type
Operator
Statement
Array
Primitive Wrapper Types
Function
Object-Oriented
Date
DOM
JSON
Regular Expressions