Understand JS stereotypes, prototype chains, and inheritance

The prototype

What is a prototype object?

  • Each 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)
Copy the code

  • Prototype is the prototype object created by calling the constructor. This instance contains a pointer to the prototype object called [[prototype]].

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

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

This is why all custom types inherit default methods like toString() and valueOf().

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

Why use prototype objects?

The advantage of using a prototype object 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
Copy the code

How do I access prototype objects?

  • Firefox, Safari, and Chrome all support one property per object__proto__Prototype objects can be accessed. ECMAScript 2015 will__proto__Attributes are incorporated as part of the specification.
console.log(p1)
console.log(p1.__proto__ === Person.prototype) // true
Copy the code

  • ES5 adds a methodObject.getPrototypeOf()To return to[[Prototype]]The value of the.
console.log(Object.getPrototypeOf(p1) === Person.prototype) // true
Copy the code

Other approaches to prototype objects:

  • The instanceof operator
console.log(p1 instanceof Person) // true
Copy the code
  • Can be achieved byisPrototypeOf()Method to determine whether there is a stereotype relationship between objects.
console.log(Person.prototype.isPrototypeOf(p2)) // true
Copy the code
  • The hasOwnProperty() method checks whether an attribute exists in an instance or a stereotype and returns true only if it exists in an instance.

  • The IN operator can be used in two ways: alone or in a for in loop. In returns true for any property that exists on the prototype or instance, and hasOwnProperty() is used to determine if the object property is on the prototype.

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

Prototype chain

A quick review of the relationship between constructors, stereotypes, and instances:

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

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

What is a prototype chain?

If we make the stereotype equal to an instance of another type, the stereotype will contain a pointer to another stereotype, which in turn will contain a pointer to another constructor. If another prototype is an instance of another type, then the above relationship still holds, and so on, the chain of instance and prototype is formed. This is the basic concept of the so-called prototype chain.

  • We already know thatp1.__proto__ === Person.prototype, thenPerson.__proto__What?

Person.__proto__ points to the prototype of the Person constructor, that is, function.prototype

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

Prototype is the prototype Object of Person, which is essentially an Object, so its __proto__ refers to the prototype of its constructor (Object), Object. Prototype

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

Object and Person are both constructors, so the constructor of Object is Function

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

Function. Prototype is the only typeof XXX whose prototype is Function. All other constructors’ prototype is an object.

  1. thenFunction.prototype.__proto__What is?
console.log(Function.prototype.__proto__ === Object.prototype) // true
Copy the code
  1. Object.prototype.__proto__What is?
console.log(Object.prototype.__proto__) // null
Copy the code

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

inheritance

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(); // Override the prototype object to inherit 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
Copy the code
  • Borrow constructors/forge objects/classical inheritance

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

Even if there are attributes in the superclass that reference the type, a copy is created in the subclass.

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

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
Copy the code

Borrowing constructor problems:

Methods are defined in constructors, so function reuse is out of the question. Methods defined in the stereotype of a supertype are also invisible to subtypes.

  • Combinatorial inheritance/pseudo-classical inheritance

Inheritance of stereotype properties and methods is implemented using stereotype chains and instance properties are inherited through constructors.

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){
	// Inherit attributes
	SuperType_3.call(this,name); // Call SuperType_3()
	this.age=age;
}
// Inheritance method
SubType_3.prototype=new SuperType_3(); // A call to SuperType_3() causes subType_3.prototype. constructor to point to SuperType_3
SubType_3.prototype.constructor=SubType_3; // Fix the constructor pointing of the prototype object
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
Copy the code

Disadvantages: Two calls to SuperType_3()

  • Prototype inheritance

ECMAScript5 adds the object.create () method, which takes two parameters (a base Object for creating a copy, and an optional Object that defines additional properties for the new Object). Supported browsers include Internet Explorer 9+, Firefox 4+, Safari 5+, Opera 12+, and 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' ]
Copy the code

Old-style inheritance is perfectly fine in cases where there is no need to create constructors, but just want one object to be similar to another.

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

  • Parasitic inheritance

Any function that can return a new object.

function object(o){
	function F(){}
	F.prototype=o; // The prototype points to O, so any changes to its prototype are also reflected on 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
Copy the code

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

Disadvantages: Adding functions to an object is inefficient because it cannot be reused

  • Parasitic combinatorial inheritance

Solve the problem of two calls.

The ideal inheritance paradigm for reference types.

function inheritPrototype(SubType,SuperType){
	var prototype=Object(SuperType.prototype); // Create a copy of the superclass prototype
	prototype.constructor=SubType;
	SubType.prototype=prototype; // Replaces the previous first call to SuperType()
}
function SuperType(name){
	this.name=name;
	this.colors=['red'.'blue'.'green'];
}
SuperType.prototype.sayName=function(){
	console.log(this.name);
};
function SubType(name,age){
	// Inherit attributes
	SuperType.call(this,name); // Call SuperType() twice
	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
Copy the code
  • Implement a parasitic combinatorial inheritance using empty functions or ES6
// Parent constructor
function Parent(name) {
	this.name = name || 'Adam';
}
// Add methods to the prototype
Parent.prototype.say = function () {
	return this.name;
};
// subconstructor
function Child(name) {
	Parent.apply(this.arguments); // Inherit attributes
}

// Function proxy avoids creating temporary function F multiple times
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 writing
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)
Copy the code

reference

JavaScript Advanced Programming

The most detailed JS prototype and prototype chain ultimate detailed explanation, no “may be” series of three


Other related articles

In-depth understanding of JavaScript prototypes

Javascript object-oriented programming – Ruan Yifeng series of three