inheritance and the prototype chain Implement inheritance

Javascript Basic review Part 4

What is Prototype chain and how to realize inheritance in Javascript

Why we need a prototype is Javascript

Problem

One of the disadvantages of using constructors to create instance objects is that they can not share properties and methods. For example, in the constructor of a DOG object, set the common attribute “species” of instance objects. Then, two instance objects are generated:

function DOG(name){
    this.name = name;
    this.species = 'Dog';
  }
var dogA = new DOG('Dog1');
var dogB = new DOG('Dog2');

These two objects' species attributes are independent, if you modify one, does not affect the other. Each instance object has its own copy of the properties and methods. This not only can not do data sharing, is also a great waste of resources.

dogA.species = 'Cat';
alert(dogB.species); // show 'Dog', not influneced by dogA

Generate

Therefore, we need a property, which contains an object (referred to as “prototype object”), all properties and methods needed to be shared are placed inside the object; those properties and methods which do not need to be shared are put inside the structure Function.
Put the DOG constructor as an example, now use the prototype property to rewrite:

function DOG(name){
    this.name = name;
  }
DOG.prototype = { species : 'Dog' };
var dogA = new DOG('Dog1');
var dogB = new DOG('Dog2');
alert(dogA.species); // Dog
alert(dogB.species); // Dog
//Then we change the prototype attribute
DOG.prototype.species = 'Cat';
alert(dogA.species); // Cat
alert(dogB.species); // Cat

由于所有的实例对象共享同一个prototype对象,那么从外界看起来,prototype对象就好像是实例对象的原型,而实例对象则好像”继承”了prototype对象一样。

The definition of Prototype Chain

When it comes to inheritance, JavaScript only has one construct: objects. Each object has an internal link to another object called its prototype. That prototype object has a prototype of its own, and so on until an object is reached with null as its prototype. Null, by definition, has no prototype, and acts as the final link in this prototype chain.

JavaScript objects are dynamic “bags” of properties (referred to as own properties). JavaScript objects have a link to a prototype object. When trying to access a property of an object, the property will not only be sought on the object but on the prototype of the object, the prototype of the prototype, and so on until either a property with a matching name is found or the end of the prototype chain is reached.

// Let's assume we have object o, with its own properties a and b:
{a: 1, b: 2}
// o.[[Prototype]] has properties b and c:
{b: 3, c: 4}
// Finally, o.[[Prototype]].[[Prototype]] is null.
// This is the end of the prototype chain as null
// Thus, the full prototype chain looks like:
// {a:1, b:2} ---> {b:3, c:4} ---> null
console.log(o.a); // 1
// Is there an 'a' own property on o? Yes, and its value is 1.
console.log(o.b); // 2
// Is there a 'b' own property on o? Yes, and its value is 2.
// The prototype also has a 'b' property, but it's not visited. 
console.log(o.c); // 4
// Is there a 'c' own property on o? No, check its prototype.
// Is there a 'c' own property on o.[[Prototype]]? Yes, its value is 4.
console.log(o.d); // undefined
// Is there a 'd' own property on o? No, check its prototype.
// Is there a 'd' own property on o.[[Prototype]]? No, check its prototype.
// o.[[Prototype]].[[Prototype]] is null, stop searching

Prototype pattern validation method

isPrototypeOf()

This method is used to determine the relationship between a prototype object and an instance.

alert(Dog.prototype.isPrototypeOf(dogA)); //true
alert(Dog.prototype.isPrototypeOf(dogB)); //true

hasOwnProperty() or in operand

Each instance object has a hasOwnProperty () method, used to determine whether a property is a local property, or inherited from the prototype object properties. Or we can use in operand.

alert(dogA.hasOwnProperty("name")); // true
alert(dogA.hasOwnProperty("species")); // false
alert("name" in dogA); // true
alert("species" in dogA); // true

Implement inheritance

Constructor inheritance

The question is that if there is a Animal object’s constructor and a Cat object’s constructor:

function Animal(){
    this.species = "Animal";
  }
function Cat(name,color){
    this.name = name;
    this.color = color;
  }

How can "Cat" inherit "Animal" function?
### Constructor binding
Use the call or apply method to bind the constructor of the parent object to the child object by adding a line to the child object constructor:

function Cat(name,color){
    Animal.apply(this, arguments);
    this.name = name;
    this.color = color;
  }
  var cat1 = new Cat("Cat1","yellow");
  alert(cat1.species); // Animal

Inheritance with the Prototype

Assign Constructor to Propotype

If the Cat object’s prototype object points to an Animal object’s instance, then all the Cat object’s instance can inherit the Animal object.

Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
var cat1 = new Cat("Cat1","yellow");
alert(cat1.species); // Animal

So Why we need the code after the first assignment:
Cat.prototype.constructor = Cat;
It turns out that any prototype object has a constructor property that points to the constructor of the origin object. If there is no "Cat.prototype = new Animal ();", Cat.prototype.constructor points to Cat; after this codes, Cat.prototype.constructor points to Animal. More importantly, each instance of this object also has a constructor property, which defaults to the constructor property of the prototype object.

Cat.prototype = new Animal();
alert(Cat.prototype.constructor == Animal); //true
alert(Cat1.constructor == Cat.prototype.constructor); // true
alert(Cat1.constructor == Animal); // true

Therefore, after running the “Cat.prototype = new Animal ();”, Cat1.constructor also points to Animal! This obviously causes the chain of inheritance to become disorganized (Cat1 is explicitly generated with the constructor Cat), so we must manually correct the Cat.prototype object’s constructor to Cat. This is the meaning of the second line.

Use an empty object as an intermediary

If we directly assign the prototype of Animal to the prototype of Cat:

function Animal(){ }
Animal.prototype.species = "Animal";
Cat.prototype = Animal.prototype;
Cat.prototype.constructor = Cat;
var cat1 = new Cat("Cat1","yellow");
alert(cat1.species); // Animal

The drawback is that Cat.prototype and Animal.prototype now point to the same object, so any changes to Cat.prototype will be reflected in Animal.prototype.Like:

Cat.prototype.constructor = Cat;
alert(Animal.prototype.constructor); // Cat

So we use an empty object as an intermediary.

var F = function(){};
F.prototype = Animal.prototype;
Cat.prototype = new F();
Cat.prototype.constructor = Cat;

Packaged as a function extend, YUI library is achieving inheritance with this method:

function extend(Child, Parent) {
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.uber = Parent.prototype;
}

Copy inheritance

We can copy all the attributes and methods of father object into the sub-object, we can achieve inheritance.

function Animal(){};
Animal.prototype.species = "Animal";
function extend2(Child, Parent) {
var p = Parent.prototype;
var c = Child.prototype;
for (var i in p) {
  c[i] = p[i];
}
c.uber = p;
}

Object inheritance

Now there is an object, called “Chinese.” There is also an object called “Doctor”. How can I get the “doctor” to inherit “Chinese”, that is, how can I create a “Chinese doctor” object?

var Chinese = {
    nation:'China'
};
var Doctor ={
    career:'Doctor'
}

object() Function

The object() function is:

function object(o) {
   function F() {}
   F.prototype = o;
   return new F();
  }

The object () function, in fact, only do one thing, point sub-object prototype to the parent object, so that the child object and the parent object are connected together in the Prototype chain.

var Doctor = object(Chinese);
alert(Doctor.nation); //China

Shallow copy

Another idea: copied all of the parent object’s attributes to the child object.

function extendCopy(p) {
   var c = {};
   for (var i in p) { 
     c[i] = p[i];
   }
   c.uber = p;
   return c;
  }
//In this example
var Doctor = extendCopy(Chinese);
Doctor.career = 'Doctor';
alert(Doctor.nation); // China

However, such a copy has a problem. That is, if the property of the parent object is equal to an array or another object, then the child object actually gets only a memory address, not a true copy, so there is a possibility that the parent object will be tampered with.Like:

Chinese.birthPlaces = ['A','B','C'];
var Doctor = extendCopy(Chinese);
Doctor.birthPlaces.push('D');
alert(Doctor.birthPlaces); //A, B, C, D
alert(Chinese.birthPlaces); //A, B, C, D

See, now to the Chinese to add a “place of birth” attribute, its value is an array. Through extendCopy () function, Doctor inherited the Chinese. Then we add a city to the Doctor’s Birthplace.Chinese’s “place of birth” has been changed!

Deep copy

The so-called “deep copy”, is able to achieve the true sense of the array and object copy. It is not difficult to achieve, as long as the recursive call “shallow copy” on the list.

function deepCopy(p, c) {
   var c = c || {};
   for (var i in p) {
     if (typeof p[i] === 'object') {
       c[i] = (p[i].constructor === Array) ? [] : {};
       deepCopy(p[i], c[i]);
     } else {
        c[i] = p[i];
     }
   }
   return c;
  }

instanceof Operator

The instanceof operator tests whether an object has in its prototype chain the prototype property of a constructor.

// defining constructors
function C(){}
function D(){}
var o = new C();
o instanceof C;// true, because: Object.getPrototypeOf(o) === C.prototype
o instanceof D;// false, because D.prototype is nowhere in o's prototype chain
o instanceof Object; // true, because:C.prototype is instanceof Object 
C.prototype = {};
var o2 = new C();
o2 instanceof C; // true
o instanceof C; // false, because C.prototype is nowhere in o's prototype chain anymore
D.prototype = new C(); // use inheritance 
var o3 = new D();// Prototype chain: o3->D->C->C.prototype->null
o3 instanceof D; // true
o3 instanceof C; // true