Object Oriented JavaScript
1 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
Constructors
Prototypes
2 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
JavaScript lacks classes, it turns to constructors and prototypes to bring a similar
order to objects.
A constructor is simply a function that is used with new to create an object.
The advantage of constructors is that objects created with the same constructor
contain the same properties and methods.
If you want to create multiple similar objects, you can create your own constructors
and therefore your own reference types.
Because a constructor is just a function, you define it in the same way.
The only difference is that constructor names should begin with a capital letter, to
distinguish them from other functions.
For example, look at the following empty Person function:
3 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
function Person() {
// intentionally empty
}
After the constructor is defined, you can start creating instances, like the following
two Person objects:
var person1 = new Person();
var person2 = new Person();
When you have no parameters to pass into your constructor, you can even omit the
parentheses:
var person1 = new Person;
var person2 = new Person;
4 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
The new operator automatically creates an object of the given type and returns it.
That also means you can use the instanceof operator to deduce an object’s type.
console.log(person1 instanceof Person); // true
console.log(person2 instanceof Person); // true
The whole point of a constructor is to make it easy to create more objects with the
same properties and methods.
To do that, simply add any properties you want to this inside of the constructor, as in
the following example:
function Person(name) {
this.name = name;
this.sayName = function() {
console.log(this.name);
};
}
5 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
There’s no need to return a value from the function because the new operator
produces the return value.
Now you can use the Person constructor to create objects with an initialized name
property:
var person1 = new Person(“Suman");
var person2 = new Person(“Rakesh");
console.log(person1.name); // “Suman"
console.log(person2.name); // “Rakesh"
person1.sayName(); // outputs “Suman"
person2.sayName(); // outputs “Rakesh"
Each object has its own name property, so sayName() should return different values
depending on the object on which you use it.
Constructors allow you to initialize an instance of a type in a consistent way,
performing all of the property setup that is necessary before the object can be used.
6 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
For example, you could also use Object.defineProperty() inside of a constructor to
help initialize the instance:
function Person(name)
{
Object.defineProperty(this, "name", {
get: function() {
return name;
},
set: function(newName) {
name = newName;
},
enumerable: true,
configurable: true
});
this.sayName = function() {
console.log(this.name); };
}
7 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
In this version of the Person constructor, the name property is an accessor property
that uses the name parameter for storing the actual name. This is possible because
named parameters act like local variables.
Make sure to always call constructors with new; otherwise, you risk changing the
global object instead of the newly created object.
Consider what happens in the following code:
var person1 = Person(“Suman"); // note: missing "new"
console.log(person1 instanceof Person); // false
console.log(typeof person1); // "undefined"
console.log(name); // “Suman"
When Person is called as a function without new, the value of this inside of the
constructor is equal to the global this object.
The variable person1 doesn’t contain a value because the Person constructor relies
on new to supply a return value.
8 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
Without new, Person is just a function without a return statement.
The assignment to this.name actually creates a global variable called name, which is
where the name passed to Person is stored.
Constructors allow you to configure object instances with the same properties, but
constructors alone don’t eliminate code redundancy.
In the example code thus far, each instance has had its own sayName() method even
though sayName() doesn’t change.
That means if you have 100 instances of an object, then there are 100 copies of a
function that do the exact same thing, just with different data.
It would be much more efficient if all of the instances shared one method, and then
that method could use this.name to retrieve the appropriate data.
This is where prototypes come in.
9 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
You can think of a prototype as a recipe for an object.
Almost every function (with the exception of some built-in functions) has a prototype
property that is used during the creation of new instances.
That prototype is shared among all of the object instances, and those instances can
access properties of the prototype
For example, the hasOwnProperty() method is defined on the generic Object
prototype, but it can be accessed from any object as if it were an own property, as
shown in this example:
var book = { title: “Norwegian Wood" };
console.log("title" in book); // true
console.log(book.hasOwnProperty("title")); // true
console.log("hasOwnProperty" in book); // true
console.log(book.hasOwnProperty("hasOwnProperty")); // false
console.log(Object.prototype.hasOwnProperty("hasOwnProperty")); // true
10 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
Even though there is no definition for hasOwnProperty() on book, that method can
still be accessed as book.hasOwnProperty() because the definition does exist on
Object.prototype.
Remember that the in operator returns true for both prototype properties and own
properties.
You can determine whether a property is on the prototype by using a function such as:
function hasPrototypeProperty(object, name) {
return name in object && !object.hasOwnProperty(name);
}
console.log(hasPrototypeProperty(book, "title")); // false
console.log(hasPrototypeProperty(book, "hasOwnProperty")); // true
11 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
The [[Prototype]] Property
An instance keeps track of its prototype through an internal property called
[[Prototype]].
This property is a pointer back to the prototype object that the instance is using.
When you create a new object using new, the constructor’s prototype property is
assigned to the [[Prototype]] property of that new object.
Following figure shows how the [[Prototype]] property lets multiple instances of an
object type refer to the same prototype, which can reduce code duplication.
12 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
The [[Prototype]] Property
13 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
The [[Prototype]] Property
You can read the value of the [[Prototype]] property by using the
Object.getPrototypeOf() method on an object.
For example, the following code checks the [[Prototype]] of a generic, empty object.
var object = {};
var prototype = Object.getPrototypeOf(object);
console.log(prototype === Object.prototype); // true
For any generic object like this one , [[Prototype]] is always a reference to
Object.prototype.
Some JavaScript engines also support a property called __proto__ on all objects. This
property allows you to both read from and write to the [[Prototype]] property. Firefox,
Safari, Chrome, and Node.js all support this property, and __proto__ is on the path for
standardization in ECMAScript 6.
14 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
The [[Prototype]] Property
You can also test to see if one object is a prototype for another by using the isPrototypeOf()
method, which is included on all objects:
var object = {};
console.log(Object.prototype.isPrototypeOf(object)); // true
Because object is just a generic object, its prototype should be Object.prototype,
meaning isPrototypeOf() should return true.
When a property is read on an object, the JavaScript engine first looks for an own
property with that name.
If the engine finds a correctly named own property, it returns that value.
If no own property with that name exists on the target object, JavaScript searches the
[[Prototype]] object instead.
15 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
The [[Prototype]] Property
If a prototype property with that name exists, the value of that property is returned.
If the search concludes without finding a property with the correct name, undefined is
returned.
Consider the following, in which an object is first created without any own properties:
var object = {};
console.log(object.toString()); // "[object Object]"
object.toString = function() {
return "[object Custom]";
};
16 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
The [[Prototype]] Property
console.log(object.toString()); // "[object Custom]“
// delete own property
delete object.toString;
console.log(object.toString()); // "[object Object]"
// no effect - delete only works on own properties
delete object.toString;
console.log(object.toString()); // "[object Object]"
In this example, the toString() method comes from the prototype and returns "[object
Object]" by default.
If you then define an own property called toString(), that own property is used
whenever toString() is called on the object again
17 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
The [[Prototype]] Property
The own property shadows the prototype property, so the prototype property of the
same name is no longer used.
If you then define an own property called toString(), that own property is used
whenever toString() is called on the object again
The prototype property is used again only if the own property is deleted from the object
Keep in mind that you can’t delete a prototype property from an instance because the
delete operator acts only on own properties
Keep in mind that you can’t delete a prototype property from an instance because the
delete operator acts only on own properties
18 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
The [[Prototype]] Property
19 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
Using Prototypes with Constructors
The shared nature of prototypes makes them ideal for defining methods once for all
objects of a given type.
Because methods tend to do the same thing for all instances, there’s no reason each
instance needs its own set of methods.
It’s much more efficient to put the methods on the prototype and then use this to
access the current instance.
For example, consider the following new Person constructor:
20 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
Using Prototypes with Constructors
function Person(name) {
this.name = name;
}
Person.prototype.sayName = function() {
console.log(this.name);
};
var person1 = new Person("Nicholas");
var person2 = new Person("Greg");
console.log(person1.name); // "Nicholas"
console.log(person2.name); // "Greg“
person1.sayName(); // outputs "Nicholas"
person2.sayName(); // outputs "Greg"
21 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
Using Prototypes with Constructors
In this version of the Person constructor, sayName() is defined on the prototype instead
of in the constructor.
The object instances work exactly the same as the example from earlier slide, even
though sayName() is now a prototype property instead of an own property.
Because person1 and person2 are each base references for their calls to sayName(), the
this value is assigned to person1 and person2, respectively.
You can also store other types of data on the prototype, but be careful when using
reference values.
Because these values are shared across instances, you might not expect one instance to
be able to change values that another instance will access.
This example shows what can happen when you don’t watch where your reference
values are pointing:
22 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
Using Prototypes with Constructors
function Person(name)
{
this.name = name;
}
Person.prototype.sayName = function() {
console.log(this.name);
};
Person.prototype.favorites = [];
var person1 = new Person("Nicholas");
var person2 = new Person("Greg");
person1.favorites.push("pizza");
person2.favorites.push("quinoa");
console.log(person1.favorites); // "pizza,quinoa"
console.log(person2.favorites); // "pizza,quinoa"
23 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
Using Prototypes with Constructors
The favorites property is defined on the prototype, which means person1.favorites and
person2.favorites point to the same array.
Any values you add to either person’s favorites will be elements in that array on the
prototype.
That may not be the behavior that you actually want, so it’s important to be very careful
about what you define on the prototype.
Even though you can add properties to the prototype one by one, many developers use
a more succinct pattern that involves replacing the prototype with an object literal:
24 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
Using Prototypes with Constructors
function Person(name)
{
this.name = name;
}
Person.prototype = {
sayName: function() {
console.log(this.name);
},
toString: function() {
return "[Person " + this.name + "]";
}
};
25 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
Using Prototypes with Constructors
This code defines two methods on the prototype, sayName() and toString() . This pattern
has become quite popular because it eliminates the need to type Person.prototype
multiple times.
26 © 2013, Cognizant Technology Solutions
Inheritance
Prototype Chaining and Object.prototype
Object Inheritance
Constructor Inheritance
Constructor Stealing
Accessing Supertype Methods
27 © 2013, Cognizant Technology Solutions
Object Patterns
Private and Privileged Members
Mixins
Scope-Safe Constructors
28 © 2013, Cognizant Technology Solutions
Thank You
29 © 2013, Cognizant Technology Solutions