javascript - object oriented programming

55
Javascript - Object-Oriented Programming @farhan-faruque

Upload: farhan-faruque

Post on 15-Jul-2015

102 views

Category:

Technology


2 download

TRANSCRIPT

Javascript - Object-Oriented Programming

@farhan-faruque

Overview!!ECMAScript has no concept of classes, and therefore objects are different than in class-based languages.ECMA-262 defines an object as an “unordered collection of properties each of which contains a primitive value, object, or function.”Each object is created based on a reference type, either one of the native types or a developer-defined type.

UNDERSTANDING OBJECTSThe simplest way to create a custom object is to create a new instance of Object and add properties and methods to it

var person = new Object();person.name = “Nicholas”;person.sayName = function(){ alert(this.name);};

can be rewritten using object literal:var person = {

name: “Nicholas”,sayName: function(){

alert(this.name);}

};

Types of PropertiesThere are two types of properties: data properties and accessor properties.Data Properties➢ [[Configurable]] - property may be redefined by removing the property via

delete, changing the property’s attributes, or changing the property into an accessor property. By default, this is true for all properties defined directly on an object.

➢ [[Enumerable]] - property will be returned in a for-in loop. By default, this is true

➢ [[Writable]] - property’s value can be changed. By default, this is true for all ➢ [[Value]] - Contains the actual data value for the property.The default value

for this attribute is undefined.

change default propertiesTo change any of the default property attributes, we must use the ECMAScript 5 Object.defineProperty() method.

var person = {};Object.defineProperty(person, “name”, {

writable: false,value: “Nicholas”

})alert(person.name); //”Nicholas”person.name = “Greg”;alert(person.name); //”Nicholas”

name “Nicholas”

Configurable propertyvar person = {};Object.defineProperty(person, “name”, {

configurable: false,value: “Nicholas”

});alert(person.name);//”Nicholas”delete person.name;alert(person.name);//”Nicholas”

setting configurable to false means that the property cannot be removed from the object.

Change Propertiesonce a property has been defined as nonconfigurable, it cannot become configurable again. Any attempt to call Object.defineProperty() and change any attribute other than writable causes an error.

Accessor PropertiesAccessor properties do not contain a data value.they contain a combination of a getter function and a setter function Accessor properties have four attributes:➢ [[Configurable]] - indicates if the property may be redefined➢ [[Enumerable]] - Indicates if the property will be returned in a

for-in loop.➢ [[Get]] - The function to call when the property is read from➢ [[Set]] - The function to call when the property is written to.

Examplevar book = {

_year: 2004,edition: 1

};

Object.defineProperty(book, “year”, {get: function(){

return this._year;},set: function(newValue){

this._year = newValue;}

});

book.year = 2005;alert(book.edition);//2005

Accessor PropertiesAssigning just a getter means that the property cannot be written to and attempts to do so will be ignored a property

with only a setter cannot be read and will return the value undefined in nonstrict mode, while doing so throws an error in strict mode.

Defining Multiple PropertiesECMAScript 5 provides the Object.defineProperties() method

var book = {};Object.defineProperties(book, {

_year: {value: 2004

},edition: {

value: 1}

});

Reading Property AttributesObject.getOwnPropertyDescriptor() - retrieve the property descriptor for a given propertyvar book = {};Object.defineProperties(book, {

_year: {value: 2004

},edition: {

value: 1}

});var descriptor = Object.getOwnPropertyDescriptor(book, “_year”);alert(descriptor.value);//2004alert(descriptor.configurable);//falsealert(typeof descriptor.get);//”undefined”

The Factory Patternfunction createPerson(name, age, job){

var o = new Object();o.name = name;o.age = age;o.job = job;o.sayName = function(){

alert(this.name);};return o;

}var person1 = createPerson(“Nicholas”, 29, “Software Engineer”);var person2 = createPerson(“Greg”, 27, “Doctor”);

The Constructor Patternfunction Person(name, age, job){

this.name = name;this.age = age;this.job = job;this.sayName = function(){

alert(this.name);};

}var person1 = new Person(“Nicholas”, 29, “Software Engineer”);var person2 = new Person(“Greg”, 27, “Doctor”);

Each of these objects has a constructor property that points back to Person

constructor propertyalert(person1.constructor == Person); //truealert(person2.constructor == Person); //true

The constructor property was originally intended for use in identifying the object type.

However, the instanceof operator is considered to be a safer way of determining type.

Problems with ConstructorsThe major downside to constructors is that methods are created once for each instance.functions are objects in ECMAScript, so every time a function is defined, it’s actually an object being instantiated. Logically, the constructor actually looks like this:function Person(name, age, job){

this.name = name;this.age = age;this.job = job;this.sayName = new Function(“alert(this.name)”); //logical equivalent

}

The Prototype PatternEach function is created with a prototype property, which is an object containing properties and methods that should be available to instances of a particular reference type.

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

Examplefunction Person(){

}Person.prototype.name = “Nicholas”;Person.prototype.age = 29;Person.prototype.job = “Software Engineer”;Person.prototype.sayName = function(){

alert(this.name);};

var person1 = new Person();person1.sayName();//”Nicholas”

var person2 = new Person();person1.sayName(); //”Nicholas”alert(person1.sayName == person2.sayName);//true

How Prototypes Work➔ Whenever a function is created, its prototype property is also

created.➔ All prototypes automatically get a property called constructor

that points back to the function on which it is a property.➔ Each time the constructor is called to create a new instance, that

instance has an internal pointer to the constructor’s prototype.

How Prototypes Work

Person

prototype

Person Prototype

constructor

name

sayName

“Farhan”

person2[[prototype]][[prototype]]

(function)

person1

How Prototypes Work

➔ Person.prototype points to the prototype object but Person.prototype.constructor points back to Person.

➔ Each instance of Person, person1, and person2 has internal properties that point back to Person.prototype only; each has no direct relationship with the constructor.

isPrototypeOf() methodisPrototypeOf()

alert(Person.prototype.isPrototypeOf(person1));//truealert(Person.prototype.isPrototypeOf(person2));//true

Object.getPrototypeOf()Object.getPrototypeOf() - which returns the value of [[Prototype]] in all supporting implementations.

alert(Object.getPrototypeOf(person1) == Person.prototype);//truealert(Object.getPrototypeOf(person1).name); //”Nicholas”

find a property in Object➔ The search begins on the object instance itself➔ If a property with the given name is found on the instance, then

that value is returned➔ If the property is not found, then the search continues up the

pointer to the prototype➔ The prototype is searched for a property with the same name. ➔ If the property is found on the prototype, then that value is

returned.

How Prototypes WorkAlthough it’s possible to read values on the prototype from object instances, it is not possible to overwrite them. function Person(){}Person.prototype.name = “Nicholas”;

var person1 = new Person();var person2 = new Person();person1.name = “Greg”;alert(person1.name); //”Greg” - from instancealert(person2.name); //”Nicholas” - from prototype

add a property to an instance that has the same name as a property on the prototype.

How Prototypes Work➔ Once a is added to the object instance, it shadows any

properties of the same name on the prototype, which means that it blocks access to the property on the prototype without altering it.

➔ setting the property to null only sets the property on the instance and doesn’t restore the link to the prototype.

➔ The delete operator, however, completely removes the instance property

How Prototypes Work - Examplefunction Person(){}Person.prototype.name = “Nicholas”;

var person1 = new Person();var person2 = new Person();person1.name = “Greg”;alert(person1.name); //”Greg” - from instancealert(person2.name); //”Nicholas” - from prototypedelete person1.name;alert(person1.name); //”Nicholas” - from the prototype

hasOwnPropertyhasOwnProperty() - determines if a property exists on the instance or on the prototype.function Person(){}Person.prototype.name = “Nicholas”;var person1 = new Person();var person2 = new Person();alert(person1.hasOwnProperty(“name”)); //falseperson1.name = “Greg”;alert(person1.hasOwnProperty(“name”)); //truealert(person2.hasOwnProperty(“name”)); //false

Prototypes and the in Operatorin true

function Person(){}Person.prototype.name = “Nicholas”;alert(person1.hasOwnProperty(“name”));//falsealert(“name” in person1); //trueperson1.name = “Greg”;alert(person2.hasOwnProperty(“name”)); //falsealert(“name” in person2); //true

Object.keys()Object.keys() method - accepts an object as its argument and returns an array of strings containing the names of all enumerable properties.

function Person(){}Person.prototype.name = “Nicholas”;Person.prototype.age = 29;Person.prototype.job = “Software Engineer”;var keys = Object.keys(Person.prototype);alert(keys); //”name,age,job,sayName”

Alternate Prototype Syntaxfunction Person(){}Person.prototype = {

name : “Nicholas”,age : 29,job : “Software Engineer”,sayName : function () {

alert(this.name);}

};

The end result is the same, with one exception: the constructor property no longer points to Person .its overwrites the default prototype object completely, meaning that the constructor property is equal to that of a completely new object (the Object constructor) instead of the function itself.

Alternate Prototype Syntaxfunction Person(){}Person.prototype = {

constructor: Person,name : “Nicholas”,age : 29,job : “Software Engineer”,sayName : function () {

alert(this.name);}

};

constructor Person

Dynamic Nature of Prototypeschanges made to the prototype at any point are immediately reflected on instances, even the instances that existed before the change was made.

var friend= new Person();Person.prototype.sayHi = function(){

alert(“hi”);};friend.sayHi(); //”hi” - works!

Dynamic Nature of PrototypesWe cannot overwrite the entire prototype and expect the same behavior.The [[Prototype]] pointer is assigned when the constructor is called, so changing the prototype to a different object severs the tie between the constructor and the original prototype.function Person(){}var friend = new Person();Person.prototype = {

constructor: Person,sayName : function () {

alert(this.name);}

};friend.sayName(); //error

Native Object PrototypesNative object prototypes can be modified just like custom object prototypes,so methods can be added at any time.

String.prototype.startsWith = function (text) {return this.indexOf(text) == 0;};var msg = “Hello world!”;alert(msg.startsWith(“Hello”)); //true

The method is assigned to String.prototype, making it available to all strings in the environment.

Problems with PrototypesAll properties on the prototype are shared among instances, which is ideal for functions.

function Person(){}Person.prototype = {

constructor: Person,age : 29,friends : [“Shelby”, “Court”]

};

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

person1.friends.push(“Van”);alert(person1.friends);//”Shelby,Court,Van”alert(person2.friends);//”Shelby,Court,Van”alert(person1.friends === person2.friends); //true

Combination Constructor/Prototype Pattern

The constructor pattern defines instance properties, whereas the prototype pattern defines methods and shared properties.

function Person(name){this.name = name;

this.friends = [“Shelby”];}

Person.prototype = {constructor: Person,sayName : function () {

alert(this.name); }};

var person1 = new Person(“Nicholas”);var person2 = new Person(“Greg”);

person1.friends.push(“Van”);alert(person1.friends);//”Shelby,Van”alert(person2.friends);//”Shelby”alert(person1.friends === person2.friends); //falsealert(person1.sayName === person2.sayName); //true

Dynamic Prototype Patternfunction Person(name){

//propertiesthis.name = name;

//methodsif (typeof this.sayName != “function”){

Person.prototype.sayName = function(){alert(this.name);};

}}

var friend = new Person(“Nicholas”);friend.sayName();

Parasitic Constructor Pattern

function Person(name, age, job){var o = new Object();o.name = name;o.age = age;o.job = job;o.sayName = function(){

alert(this.name);};return o;

}var friend = new Person(“Nicholas”, 29, “Software Engineer”);friend.sayName(); //”Nicholas

Durable Constructor Pattern

this

function Person(name, age, job){//create the object to returnvar o = new Object();//optional: define private variables/functions here//attach methodso.sayName = function(){

alert(name);};//return the objectreturn o;

}

A durable constructor vs parasitic constructor

In durable constructor - instance methods on the created object don’t refer to this- he constructor is never called using the new operator.

Prototype ChainingThe basic idea is to use the concept of prototypes to inherit properties and methods between two reference types.What if the prototype were actually an instance of another type?

- the prototype itself would have a pointer to a different prototype that, in turn,would have a pointer to another constructor.

If that prototype were also an instance of another type, then the pattern would continue, forming a chain between instances and prototypes.

Examplefunction SuperType(){

this.property = true;}

SuperType.prototype.getSuperValue = function(){return this.property;

};

function SubType(){this.subproperty = false;

}//inherit from SuperTypeSubType.prototype = new SuperType();SubType.prototype.getSubValue = function (){

return this.subproperty;};var instance = new SubType();alert(instance.getSuperValue());//true

Default PrototypesThe default prototype for any function is an instance of Object, meaning that its internal prototype pointer points to Object.prototype.This is how custom types inherit all of the default methods such as toString()and valueOf()

Prototype and Instance Relationships

The relationship between prototypes and instances is discernible in two ways. use the instanceof operator - returns true whenever an instance is used with a constructor that appears in its prototype chain

alert(instance instanceof Object); //truealert(instance instanceof SuperType); //truealert(instance instanceof SubType);//true

use the isPrototypeOf() method -

alert(Object.prototype.isPrototypeOf(instance));//truealert(SuperType.prototype.isPrototypeOf(instance));//truealert(SubType.prototype.isPrototypeOf(instance));//true

Working with Methodsoverride a supertype method or introduce new methods

- the methods must be added to the prototype after the prototype has been assigned.

...//inherit from SuperTypeSubType.prototype = new SuperType();//new methodSubType.prototype.getSubValue = function (){

return this.subproperty;};//override existing methodSubType.prototype.getSuperValue = function (){

return false;};

Problems with Prototype Chaining

The major issue revolves around prototypes that contain reference valuesfunction SuperType(){

this.colors = [“red”, “blue”, “green”];}

function SubType(){}

//inherit from SuperTypeSubType.prototype = new SuperType();var instance1 = new SubType();

instance1.colors.push(“black”);alert(instance1.colors);//”red,blue,green,black”var instance2 = new SubType();alert(instance2.colors);//”red,blue,green,black”

Problems with Prototype Chaining

we cannot pass arguments into the supertype constructor when the subtype instance is being created. In fact, there is no way to pass arguments into the supertype constructor without affecting all of the object instances.

Constructor Stealingthe apply() and call() methods can be used to execute a constructor on the newly created object.

function SuperType(){ this.colors = [“red”, “blue”, “green”];}function SubType(){

//inherit from SuperTypeSuperType.call(this);

}

var instance1 = new SubType();instance1.colors.push(“black”);alert(instance1.colors);//”red,blue,green,black”var instance2 = new SubType();alert(instance2.colors);//”red,blue,green”

Constructor Stealing - Passing Argumentsfunction SuperType(name){

this.name = name;}

function SubType(){//inherit from SuperType passing in an argumentSuperType.call(this, “Nicholas”);//instance propertythis.age = 29;

}var instance = new SubType();alert(instance.name);//”Nicholas”;alert(instance.age);//29

Problems with Constructor StealingMethods must be defined inside the constructor, so there’s no function reuse. Furthermore, methods defined on the supertype’s prototype are not accessible on the subtype, so all types can use only the constructor pattern

Combination InheritanceCombination inheritance (sometimes also called pseudoclassical inheritance) combines prototype chaining and constructor stealing to get the best of each approach

Addressing the downsides of both prototype chaining and constructor stealing, combination inheritance is the most frequently used inheritance pattern in JavaScript.

Examplefunction SuperType(name){

this.name = name;this.colors = [“red”, “blue”, “green”];

}

SuperType.prototype.sayName = function(){alert(this.name);

};

function SubType(name, age){//inherit propertiesSuperType.call(this, name);this.age = age;

}//inherit methodsSubType.prototype = new SuperType();

Prototypal Inheritancevar person = {name: “Nicholas”,friends: [“Shelby”, “Court”, “Van”]};

var anotherPerson = Object.create(person);anotherPerson.name = “Greg”;anotherPerson.friends.push(“Rob”);

var yetAnotherPerson = Object.create(person);yetAnotherPerson.name = “Linda”;yetAnotherPerson.friends.push(“Barbie”);

alert(person.friends);//”Shelby,Court,Van,Rob,Barbie”

Prototypal inheritance is useful when there is no need for the overhead of creating separate constructors, but you still need an object to behave similarly to another.

Parasitic Inheritancecreate a function that does the inheritance, augments the object in some way, and then returns the object as if it did all the work

function createAnother(original){var clone = object(original); //create a new object by calling a functionclone.sayHi = function(){

alert(“hi”);};return clone;

}

var person = {name: “Nicholas”,friends: [“Shelby”, “Court”, “Van”]

};

var anotherPerson = createAnother(person);anotherPerson.sayHi(); //”hi”