Object Oriented JavaScript
1 © 2013, Cognizant Technology Solutions
What will be covered?
Primitive and Reference Types
Functions
Understanding Objects
Constructors and Prototypes
Inheritance
Object Patterns
2 © 2013, Cognizant Technology Solutions
Understanding Objects
Defining Properties
Detecting Properties
Removing Properties
Enumeration
Property Access
Types of Properties
Property Attributes
Preventing Object Modification
3 © 2013, Cognizant Technology Solutions
Understanding Objects
Even though there are a number of built in reference types in JavaScript, you will most
likely create your own objects fairly frequently.
objects in JavaScript are dynamic, meaning that they can change at any point during
code execution.
Whereas class-based languages lock down objects based on a class definition,
JavaScript objects have no such restrictions.
4 © 2013, Cognizant Technology Solutions
Understanding Objects
Defining Properties
There are two basic ways to create your own objects: using the Object constructor
and using an object literal.
var person1 = { name: “Suman" };
var person2 = new Object();
person2.name = “Suman";
person1.age = “234";
person2.age = “111";
person1.name = “Krishna";
person2.name = “Kumar";
5 © 2013, Cognizant Technology Solutions
Understanding Objects
Defining Properties
When a property is first added to an object, JavaScript uses an internal method called
[[Put]] on the object.
The [[Put]] method creates a spot in the object to store the property.
The result of calling [[Put]] is the creation of an own property on the object.
An own property simply indicates that the specific instance of the object owns that
property.
When a new value is assigned to an existing property, a separate operation called
[[Set]] takes place.
This operation replaces the current value of the property with the new one.
6 © 2013, Cognizant Technology Solutions
Understanding Objects
Detecting Properties
Because properties can be added at any time, it’s sometimes necessary to check
whether a property exists in the object.
JavaScript developers often incorrectly use patterns like the following to detect
whether a property exists:
// unreliable
if (person1.age) {
// do something with age
}
The problem with this pattern is how JavaScript’s type coercion affects the outcome.
The if condition evaluates to true if the value is truthy (an object, a nonempty string,
a nonzero number, or true) and evaluates to false if the value is falsy (null, undefined,
0, false, NaN, or an empty string).
7 © 2013, Cognizant Technology Solutions
Understanding Objects
Detecting Properties
Because an object property can contain one of these falsy values, the example code
can yield false negatives.
For instance, if person1.age is 0, then the if condition will not be met even though the
property exists.
A more reliable way to test for the existence of a property is with the in operator
The in operator looks for a property with a given name in a specific object and returns
true if it finds it
console.log("name" in person1); // true
console.log("age" in person1); // true
console.log("title" in person1); // false
8 © 2013, Cognizant Technology Solutions
Understanding Objects
Detecting Properties
Keep in mind that methods are just properties that reference functions, so you can
check for the existence of a method in the same way.
The following adds a new function, sayName(), to person1 and uses in to confirm the
function’s presence.
var person1 = {
name: “Suman",
sayName: function() {
console.log(this.name);
}
};
console.log("sayName" in person1); // true
In most cases, the in operator is the best way to determine whether the property
exists in an object.
9 © 2013, Cognizant Technology Solutions
Understanding Objects
Detecting Properties
It has the added benefit of not evaluating the value of the property, which can be
important if such an evaluation is likely to cause a performance issue or an error.
In some cases, however, you might want to check for the existence of a property only
if it is an own property.
The in operator checks for both own properties and prototype properties.
We have hasOwnProperty() method, which is present on all objects and returns true
only if the given property exists and is an own property.
For example, the following code compares the results of using in versus
hasOwnProperty() on different properties in person1:
10 © 2013, Cognizant Technology Solutions
Understanding Objects
Detecting Properties
var person1 = {
name: "Nicholas",
sayName: function() {
console.log(this.name);
} };
console.log("name" in person1); // true
console.log(person1.hasOwnProperty("name")); // true
console.log("toString" in person1); // true
console.log(person1.hasOwnProperty("toString")); // false
The toString() method, however, is a prototype property that is present on all objects.
11 © 2013, Cognizant Technology Solutions
Understanding Objects
Removing Properties
Just as properties can be added to objects at any time, they can also be removed.
Simply setting a property to null doesn’t actually remove the property completely
from the object.
Such an operation calls [[Set]] with a value of null, which you saw earlier in the
session, only replaces the value of the property.
You need to use the delete operator to completely remove a property from an object.
The delete operator works on a single object property and calls an internal operation
named [[Delete]].
When the delete operator is successful, it returns true.
12 © 2013, Cognizant Technology Solutions
Understanding Objects
Removing Properties
var person1 = {
name: “Suman"
};
console.log("name" in person1); // true
delete person1.name; // true
console.log("name" in person1); // false
console.log(person1.name); // undefined
13 © 2013, Cognizant Technology Solutions
Understanding Objects
Enumeration
By default, all properties that you add to an object are enumerable, which means that
you can iterate over them using a for-in loop.
Enumerable properties have their internal [[Enumerable]] attributes set to true.
var property;
for (property in object)
{
console.log("Name: " + property);
console.log("Value: " + object[property]);
}
Each time through the for-in loop, the property variable is filled with the next
enumerable property on the object until all such properties have been used.
14 © 2013, Cognizant Technology Solutions
Understanding Objects
Enumeration
If you just need a list of an object’s properties to use later in your program, ECMAScript
5 introduced the Object.keys() method to retrieve an array of enumerable property
names
var properties = Object.keys(object);
// if you want to mimic for-in behavior
var i, len;
for (i=0, len=properties.length; i < len; i++){
console.log("Name: " + properties[i]);
console.log("Value: " + object[properties[i]]);
}
Typically, you would use Object.keys() in situations where you want to operate on an
array of property names and for-in when you don’t need an array.
15 © 2013, Cognizant Technology Solutions
Understanding Objects
Enumeration
Keep in mind that not all properties are enumerable. In fact, most of the native
methods on objects have their [[Enumerable]] attribute set to false.
You can check whether a property is enumerable by using the propertyIsEnumerable()
method, which is present on every object
var person1 = { name: “Suman" };
console.log("name" in person1); // true
console.log(person1.propertyIsEnumerable("name")); // true
var properties = Object.keys(person1);
console.log("length" in properties); // true
console.log(properties.propertyIsEnumerable("length")); // false
16 © 2013, Cognizant Technology Solutions
Understanding Objects
Types of Properties
There are two different types of properties: data properties and accessor properties.
Data properties contain a value, like the name property from earlier examples
Accessor properties don’t contain a value but instead define a function to call when the
property is read (called a getter), and a function to call when the property is written to
(called a setter).
Accessor properties only require either a getter or a setter, though they can have both.
There is a special syntax to define an accessor property using an object literal:
17 © 2013, Cognizant Technology Solutions
Understanding Objects
Types of Properties
var person1 = {
_name: “Suman",
get name() {
console.log("Reading name");
return this._name;
},
set name(value) {
console.log("Setting name to %s", value);
this._name = value; }
};
console.log(person1.name); // "Reading name" then “Suman"
person1.name = “Mishra";
console.log(person1.name); // "Setting name to Mishra" then “Mishra"
18 © 2013, Cognizant Technology Solutions
Understanding Objects
Types of Properties
This example defines an accessor property called name.
There is a data property called _name that contains the actual value for the property
(The leading underscore is a common convention to indicate that the property is
considered to be private, though in reality it is still public.)
The special keywords get and set are used before the accessor property name,
followed by parentheses and a function body.
Getters are expected to return a value, while setters receive the value being assigned
to the property as an argument
Accessor properties are most useful when you want the assignment of a value to
trigger some sort of behavior, or when reading a value requires the calculation of the
desired return value.
19 © 2013, Cognizant Technology Solutions
Understanding Objects
Property Attributes
Prior to ECMAScript 5, there was no way to specify whether a property should be
enumerable.
ECMAScript 5 changed this by introducing several ways of interacting with property
attributes directly, as well as introducing new attributes to support additional
functionality.
Common Attributes
There are two property attributes shared between data and accessor properties.
One is [[Enumerable]], which determines whether you can iterate over the property.
The other is [[Configurable]], which determines whether the property can be
changed.
20 © 2013, Cognizant Technology Solutions
Understanding Objects
Property Attributes
By default, all properties you declare on an object are both enumerable and
configurable.
If you want to change property attributes, you can use the Object.defineProperty()
method.
This method accepts three arguments: the object that owns the property, the property
name, and a property descriptor object containing the attributes to set.
The descriptor has properties with the same name as the internal attributes but
without the square brackets.
So you use enumerable to set [[Enumerable]], and configurable to set [[Configurable]].
21 © 2013, Cognizant Technology Solutions
Understanding Objects
Property Attributes
For example, suppose you want to make an object property nonenumerable and
nonconfigurable:
var person1 = {
name: “Suman“
};
Object.defineProperty(person1, "name", { enumerable: false });
console.log("name" in person1); // true
console.log(person1.propertyIsEnumerable("name")); // false
22 © 2013, Cognizant Technology Solutions
Understanding Objects
Property Attributes
var properties = Object.keys(person1);
console.log(properties.length); // 0
Object.defineProperty(person1, "name", { configurable: false });
// try to delete the Property
delete person1.name;
When JavaScript is running in strict mode, attempting to delete a nonconfigurable property
results in an error. In nonstrict mode, the operation silently fails.
console.log("name" in person1); // true
console.log(person1.name); // “Suman"
Object.defineProperty(person1, "name", { // error!!!
configurable: true });
23 © 2013, Cognizant Technology Solutions
Understanding Objects
Data Property Attributes
Data properties possess two additional attributes that accessors do not.
The first is [[Value]], which holds the property value.
This attribute is filled in automatically when you create a property on an object.
All property values are stored in [[Value]], even if the value is a function.
The second attribute is [[Writable]], which is a Boolean value indicating whether the
property can be written to. By default, all properties are writable unless you specify
otherwise.
With these two additional attributes, you can fully define a data property using
Object.defineProperty() even if the property doesn’t already exist. Consider this code:
24 © 2013, Cognizant Technology Solutions
Understanding Objects
Data Property Attributes
var person1 = { name: “Suman" };
You’ve seen this snippet throughout this session; it adds the name property to person1
and sets its value. You can achieve the same result using the following (more verbose)
code
var person1 = {};
Object.defineProperty(person1, "name", {
value: “Suman",
enumerable: true,
configurable: true,
writable: true
});
25 © 2013, Cognizant Technology Solutions
Understanding Objects
Data Property Attributes
When Object.defineProperty() is called, it first checks to see if the property exists. If
the property doesn’t exist, a new one is added with the attributes specified in the
descriptor.
In this case, name isn’t already a property of person1, so it is created.
When you are defining a new property with Object.defineProperty(), it’s important to
specify all of the attributes because Boolean attributes automatically default to false
otherwise.
For example, the following code creates a name property that is nonenumerable,
nonconfigurable, and nonwritable because it doesn’t explicitly make any of those
attributes true in the call to Object.defineProperty().
26 © 2013, Cognizant Technology Solutions
Understanding Objects
Data Property Attributes
var person1 = {};
Object.defineProperty(person1, "name", { value: “Suman" });
console.log("name" in person1); // true
console.log(person1.propertyIsEnumerable("name")); // false
delete person1.name;
console.log("name" in person1); // true
person1.name = "Greg";
Nonwritable properties throw an error in strict mode when you try to
change the value. In nonstrict mode, the operation silently fails.
console.log(person1.name); // “Suman"
27 © 2013, Cognizant Technology Solutions
Understanding Objects
Accessor Property Attributes
Accessor properties also have two additional attributes.
Because there is no value stored for accessor properties, there is no need for [[Value]]
or [[Writable]].
Instead, accessors have [[Get]] and [[Set]], which contain the getter and setter
functions, respectively.
The advantage of using accessor property attributes instead of object literal notation to
define accessor properties is that you can also define those properties on existing
objects.
If you want to use object literal notation, you have to define accessor properties when
you create the object.
As with data properties, you can also specify whether accessor properties are
configurable or enumerable.
28 © 2013, Cognizant Technology Solutions
Understanding Objects
Accessor Property Attributes
var person1 = {
_name: “Suman",
get name() {
console.log("Reading name"); return
this._name;
},
set name(value) {
console.log("Setting name to ", value);
this._name = value;
}
};
This code can also be written as follows:
29 © 2013, Cognizant Technology Solutions
Understanding Objects
Accessor Property Attributes
var person1 = { _name: “Suman" };
Object.defineProperty(person1, "name", {
get: function() {
console.log("Reading name");
return this._name;
},
set: function(value) {
console.log("Setting name to ", value);
this._name = value; },
enumerable: true,
configurable: true
});
30 © 2013, Cognizant Technology Solutions
Understanding Objects
Accessor Property Attributes
For example, you can create a nonconfigurable, nonenumerable, nonwritable
property like this:
var person1 = { _name: “Suman" };
Object.defineProperty(person1, "name", {
get: function() {
console.log("Reading name");
return this._name;
}
});
console.log("name" in person1); // true
console.log(person1.propertyIsEnumerable("name")); // false
delete person1.name;
console.log("name" in person1); // true
person1.name = "Greg";
console.log(person1.name); // “Suman"
31 © 2013, Cognizant Technology Solutions
Understanding Objects
Defining Multiple Properties
It’s also possible to define multiple properties on an object simultaneously if you use
Object.defineProperties() instead of Object.defineProperty().
This method accepts two arguments: the object to work on and an object containing
all of the property information.
The keys of that second argument are property names, and the values are descriptor
objects defining the attributes for those properties.
For example, the following code defines two properties:
32 © 2013, Cognizant Technology Solutions
Understanding Objects
Defining Multiple Properties
var person1 = {}; // accessor property
Object.defineProperties(person1, { name: {
get: function() {
// data property to store data console.log("Reading name");
_name: { return this._name;
value: “Suman", },
enumerable: true, set: function(value) {
configurable: true, console.log("Setting name to ", value);
writable: true this._name = value; },
}, enumerable: true,
configurable: true
}
});
33 © 2013, Cognizant Technology Solutions
Understanding Objects
Retrieving Property Attributes
If you need to fetch property attributes, you can do so in JavaScript by using
Object.getOwnPropertyDescriptor().
This method accepts two arguments: the object to work on and the property name to
retrieve.
If the property exists, you should receive a descriptor object with four properties:
configurable, enumerable, and the two others appropriate for the type of property.
For example, this code creates a property and checks its attributes:
34 © 2013, Cognizant Technology Solutions
Understanding Objects
Retrieving Property Attributes
var person1 = { name: “Suman" };
var descriptor = Object.getOwnPropertyDescriptor(person1, "name");
console.log(descriptor.enumerable); // true
console.log(descriptor.configurable); // true
console.log(descriptor.writable); // true
console.log(descriptor.value); // “Suman"
35 © 2013, Cognizant Technology Solutions
Understanding Objects
Preventing Object Modification
Objects, just like properties, have internal attributes that govern their behavior.
One of these attributes is [[Extensible]], which is a Boolean value indicating if the
object itself can be modified.
All objects you create are extensible by default, meaning new properties can be
added to the object at any time.
By setting [[Extensible]] to false, you can prevent new properties from being added to
an object.
There are two different ways to accomplish this.
36 © 2013, Cognizant Technology Solutions
Understanding Objects
Preventing Object Modification
Preventing Extensions
One way to create a nonextensible object is with Object.preventExtensions().
This method accepts a single argument, which is the object you want to make
nonextensible.
Once you use this method on an object, you’ll never be able to add any new
properties to it again.
You can check the value of [[Extensible]] by using Object.isExtensible().
The following code shows examples of both methods at work.
37 © 2013, Cognizant Technology Solutions
Understanding Objects
Preventing Object Modification
Preventing Extensions
var person1 = { name: “Suman" };
console.log(Object.isExtensible(person1)); // true
Object.preventExtensions(person1);
console.log(Object.isExtensible(person1)); // false
person1.sayName = function() {
console.log(this.name);
};
console.log("sayName" in person1); // false
38 © 2013, Cognizant Technology Solutions
Understanding Objects
Preventing Object Modification
Sealing Objects
The second way to create a nonextensible object is to seal the object.
A sealed object is nonextensible, and all of its properties are nonconfigurable.
That means not only can you not add new properties to the object, but you also
can’t remove properties
You can use the Object.seal() method on an object to seal it.
When that happens, the [[Extensible]] attribute is set to false, and all properties
have their [[Configurable]] attribute set to false.
39 © 2013, Cognizant Technology Solutions
Understanding Objects
Preventing Object Modification
Sealing Objects
var person1 = { name: “Suman" };
console.log(Object.isExtensible(person1)); // true console.log(Object.isSealed(person1)); // false
Object.seal(person1);
console.log(Object.isExtensible(person1)); // false
console.log(Object.isSealed(person1)); // true
person1.sayName = function() { console.log(this.name); };
console.log("sayName" in person1); // false
person1.name = "Greg";
console.log(person1.name); // "Greg"
delete person1.name;
console.log("name" in person1); // true
console.log(person1.name); // "Greg"
var descriptor = Object.getOwnPropertyDescriptor(person1, "name");
console.log(descriptor.configurable); // false
40 © 2013, Cognizant Technology Solutions
Constructors and Prototypes
Constructors
Prototypes
41 © 2013, Cognizant Technology Solutions
Thank You
42 © 2013, Cognizant Technology Solutions