Understand js prototype, prototype chain and inheritance

Understand js prototype, prototype chain and inheritance

Understand js prototype, prototype chain and inheritance

prototype

What is a prototype object?

  • Every function we create has a prototype property, which is a pointer to an object, the prototype object.
function Person(){}
Person.prototype.name="Jiuto";
Person.prototype.age=25;
Person.prototype.sayName=function(){
    console.log(this.name)
};
console.log(Person.prototype)
 

  • Prototype is the prototype object of the object instance created by calling the constructor [[Prototype]]. The pointer to the prototype object contained in this instance is called .

  • By default, all prototype objects will automatically get a constructor property, which is a pointer to the function where the prototype property is located.

console.log(Person.prototype.constructor) //Person
console.log(p1.constructor === Person) //true
 
  • The default prototype of all functions is an instance of Object, so the default prototype will contain an internal pointer to point to Object.prototype.

This is also the fundamental reason why all custom types inherit default methods such as toString() and valueOf().

console.log(typeof Person.prototype)
console.log(p1 instanceof Object)
console.log(p1.toString())
console.log(p1.valueOf())
 

Why use prototype objects?

The advantage of using prototype objects is that all object instances can share the properties and methods it contains.

var p1 = new Person();
var p2 = new Person();
console.log(p1.sayName === p2.sayName) //true
 

How to access the prototype object?

  • Firefox, Safari, and Chrome support an attribute on each object to __proto__access the prototype object. ECMAScript 2015 included __proto__attributes as part of the specification.
console.log(p1)
console.log(p1.__proto__ === Person.prototype) //true
 

  • ES5 adds a method Object.getPrototypeOf()to return [[Prototype]]the value.
console.log(Object.getPrototypeOf(p1) === Person.prototype) //true
 

Other methods about prototype objects:

  • instanceof operator
console.log(p1 instanceof Person) //true
 
  • You can use isPrototypeOf()methods to determine whether there is a prototype relationship between objects.
console.log(Person.prototype.isPrototypeOf(p2)) //true
 
  • hasOwnProperty()The method can detect whether a property exists in the instance or the prototype, and returns true only when it exists in the instance.

  • The in operator can be used in two ways: alone or in a for inloop. Regardless of the properties that exist on the prototype or the instance, in will return true, and it hasOwnProperty()can be combined to determine whether the object properties are on the prototype object.

p1.job = "engineer";
console.log(p1.hasOwnProperty("name")) //false
console.log("name" in p1) //true
console.log("name" in p1 && !p1.hasOwnProperty("name")) //true
console.log("job" in p1 && !p1.hasOwnProperty("job")) //false
 

Prototype chain

Briefly review the relationship between constructor, prototype and instance:

Each constructor has a prototype object, the prototype object contains a pointer to the constructor, and the instance contains an internal pointer to the prototype object.

console.log(Person.prototype.constructor === p2.constructor) //true
 

What is the prototype chain?

If we make the prototype object equal to an instance of another type, the prototype object at this time will contain a pointer to another prototype, and accordingly, the other prototype also contains a pointer to another constructor. If another archetype is another type of instance, then the above relationship is still valid, and this level of progression constitutes a chain of instances and prototypes. This is the basic concept of the so-called prototype chain.

  • We already know the above p1.__proto__ === Person.prototype, so Person.__proto__what?

Person.__proto__Points to the prototype of Person s constructor, which isFunction.prototype

console.log(Person.__proto__ === Function.prototype) //true
 
  • Person.prototype.__proto__What is it?

Person.prototypeIt is the prototype object of Person, which is essentially an object, so this object __proto__points to the prototype of its constructor (that is, Object), which isObject.prototype

console.log(Person.prototype.__proto__ === Object.prototype) //true
 
  • Object.__proto__What is it?

Object is actually no different from Person, both are constructors, so the constructor of Object is Function

console.log(Object.__proto__ === Function.prototype) //true
 
  • Some special cases (memory)
  1. Function.__ptoto__What is it?
console.log(Function.__proto__ === Function.prototype) //true
 
  1. Function.prototypeWhat type?
console.log(typeof Function.prototype) //function
 

Function.prototypeIs the only one typeof XXX.prototypeof functionthe prototype, the other constructor is a prototype object.

  1. So what Function.prototype.__proto__is it?
console.log(Function.prototype.__proto__ === Object.prototype) //true
 
  1. Object.prototype__proto__What is it?
console.log(Object.prototype.__proto__) //null
 

This is already the top of the prototype chain, pointing to null

inherit

The essence of implementing inheritance is to rewrite the prototype object and replace it with an instance of a new type.

function SuperType_1(){
	this.superproterty=true;
}
SuperType_1.prototype.getSuperValue=function(){
	return this.superproterty;
}

function SubType_1(){
	this.subproperty=false;
}
SubType_1.prototype=new SuperType_1(); // SuperType_1
SubType_1.prototype.getSubValue=function(){
	return this.subproperty;
}

var instance=new SubType_1();
console.log(instance.getSuperValue());//true
 
console.log(instance instanceof Object);//true
console.log(instance instanceof SuperType_1);//true
console.log(instance instanceof SubType_1);//true

console.log(Object.prototype.isPrototypeOf(instance));//true
console.log(SubType_1.prototype.isPrototypeOf(instance));//true
console.log(SuperType_1.prototype.isPrototypeOf(instance));//true
 
  • Borrowing constructor/fake objects/classic inheritance

Call the superclass constructor inside the subclass constructor, using apply(), call().

Even if there are attributes of reference types in the superclass, a copy will be created in the subclass.

function SuperType_2(){
	this.colors=['red','blue','green'];
}
function SubType_2(){
	SuperType_2.call(this); // SuperType_2
}

var instance1=new SubType_2();
instance1.colors.push('black');
console.log(instance1.colors);//[ 'red', 'blue', 'green', 'black' ]

var instance2=new SubType_2();
console.log(instance2.colors);//[ 'red', 'blue', 'green' ]
 
console.log(instance1 instanceof Object);//true
console.log(instance1 instanceof SuperType_2);//false
console.log(instance1 instanceof SubType_2);//true
console.log(Object.prototype.isPrototypeOf(instance1));//true
console.log(SuperType_2.prototype.isPrototypeOf(instance1));//false
console.log(SubType_2.prototype.isPrototypeOf(instance1));//true
 

Problems with borrowing constructors:

The methods are all defined in the constructor, so function reuse is out of the question. The methods defined in the prototype of the supertype are also invisible to the subtype.

  • Combined inheritance/pseudo-classical inheritance

Use the prototype chain to realize the inheritance of prototype attributes and methods, and realize the inheritance of instance attributes through the constructor.

function SuperType_3(name){
	this.name=name;
	this.colors=['red','blue','green'];
}
SuperType_3.prototype.sayName=function(){
	console.log(this.name);
};

function SubType_3(name,age){
	//
	SuperType_3.call(this,name); // SuperType_3()
	this.age=age;
}
//
SubType_3.prototype=new SuperType_3(); // SuperType_3() SubType_3.prototype.constructor SuperType_3
SubType_3.prototype.constructor=SubType_3; // 
SubType_3.prototype.sayAge=function(){
	console.log(this.age);
};
 
var instance3=new SubType_3('Nicholas',29);
instance3.colors.push('brown');
console.log(instance3.colors);//[ 'red', 'blue', 'green', 'brown' ]
instance3.sayAge();//29
instance3.sayName();//Nicholas
 
var instance4=new SubType_3('Greg',27);
console.log(instance4.colors);//[ 'red', 'blue', 'green' ]
instance4.sayName();//Greg
instance4.sayAge();//27
 
console.log(instance4 instanceof Object);//true
console.log(instance4 instanceof SuperType_3);//true
console.log(instance4 instanceof SubType_3);//true
console.log(Object.prototype.isPrototypeOf(instance4));//true
console.log(SubType_3.prototype.isPrototypeOf(instance4));//true
console.log(SuperType_3.prototype.isPrototypeOf(instance4));//true
 

Disadvantage: Call SuperType_3() twice

  • Prototype inheritance

ECMAScript5 adds the Object.create() method, which receives two parameters (the basic object used to create a copy, and the optional object that defines additional properties for the new object). The supported browsers are IE 9+, Firefox 4+, Safari 5+, Opera 12+, Chrome.

var person={
	name:'Nicholas',
	friends:['a','b','c']
};
 
var aperson=Object.create(person);
aperson.name='Greg';
aperson.friends.push('d');
 
var anotherperson=Object.create(person,{name:{value:'ccc'}});
 
console.log(person.friends);//[ 'a', 'b', 'c', 'd' ]
console.log(anotherperson.name);//ccc
console.log(anotherperson.friends);//[ 'a', 'b', 'c', 'd' ]
 

In the case where there is no need to rush to create a constructor, but just want to keep one object similar to another, prototypal inheritance is perfectly capable.

Attributes that contain reference type values always share the corresponding value.

  • Parasitic inheritance

Any function that can return a new object is suitable.

function object(o){
	function F(){}
	F.prototype=o; // o o 
	return new F();
}
function createAnother(originaobj){
	var clone=object(originaobj);
	clone.sayHi=function(){
		console.log('hi');
	};
	return clone;
}

var person={
	name:'Nicholas',
	friends:['a','b','c']
};
var yetAnotherPerson=createAnother(person);
yetAnotherPerson.sayHi();//hi
 

Parasitic inheritance is also a useful pattern in situations where objects are primarily considered rather than custom types and constructors.

Disadvantages: adding functions to objects will reduce efficiency due to the inability to reuse functions

  • Parasitic combined inheritance

Solve the problem of two calls.

The most ideal inheritance paradigm for reference types.

function inheritPrototype(SubType,SuperType){
	var prototype=Object(SuperType.prototype); // 
	prototype.constructor=SubType;
	SubType.prototype=prototype; // SuperType()
}
function SuperType(name){
	this.name=name;
	this.colors=['red','blue','green'];
}
SuperType.prototype.sayName=function(){
	console.log(this.name);
};
function SubType(name,age){
	//
	SuperType.call(this,name); // SuperType()
	this.age=age;
}
inheritPrototype(SubType,SuperType);
SubType.prototype.sayAge=function(){
	console.log(this.age);
}

var instance5=new SubType('Nicholas',29);
instance5.colors.push('brown');
console.log(instance5.colors);//[ 'red', 'blue', 'green', 'brown' ]
instance5.sayAge();//29
instance5.sayName();//Nicholas
 
var instance6=new SubType('Greg',27);
console.log(instance6.colors);//[ 'red', 'blue', 'green' ]
instance6.sayName();//Greg
instance6.sayAge();//27
 
console.log(instance6 instanceof Object);//true
console.log(instance6 instanceof SuperType);//true
console.log(instance6 instanceof SubType);//true
console.log(Object.prototype.isPrototypeOf(instance6));//true
console.log(SubType.prototype.isPrototypeOf(instance6));//true
console.log(SuperType.prototype.isPrototypeOf(instance6));//true
 
  • Use empty functions or es6 to implement a parasitic combined inheritance
//
function Parent(name) {
	this.name = name || 'Adam';
}
//
Parent.prototype.say = function () {
	return this.name;
};
//
function Child(name) {
	Parent.apply(this, arguments); // 
}

//  F
var inherit = (function () {
	var F = function () {};
	return function (C, P) {
		F.prototype = P.prototype;
		C.prototype = new F();
		C.uber = P.prototype;
		C.prototype.constructor = C;
	}
}());

inherit(Child, Parent);
var kid = new Child();
console.log(kid.constructor)
console.log(kid)

//es6 
function inherit2(subType, superType) {
	subType.prototype = Object.create(superType.prototype, {
		constructor: {
			enumerable: false,
			configurable: true,
			writable: true,
			value: subType
		}
	})
	Object.setPrototypeOf(subType, superType)
}

inherit2(Child, Parent);
var c = new Child('aha');
console.log(c.say())
console.log(c)
 

reference

"JavaScript Advanced Programming"

The most detailed JS prototype and the ultimate detailed explanation of the prototype chain, there is no "maybe" series of three articles


Other related articles

In-depth understanding of JavaScript prototypes

Javascript object-oriented programming-Ruan Yifeng series of three articles