What is a prototype chain

When we call a property of an object, if the object doesn’t have the property, the JavaScript interpreter follows __proto__ to find the property, and if the property isn’t on the prototype, to find the prototype’s prototype. This mechanism for attribute lookup is called the Prototype chain

The __proto__ attribute of an instance, pointing to the constructor’s prototype object, where properties and methods are stored that are common to all instances.

The first thing to know

  • Js is divided into function objects and ordinary objects. Each object has a __proto__ attribute, but only function objects have a Prototype attribute
  • Object and Function are built-in js functions, similar to Array, RegExp, Date, Boolean, Number, String

Prototype chain

1. Each function has a prototype property that points to the function’s prototype object

2. Each object has a **proto attribute that points to the object’s prototype object **

3. Functions are also objects

4. Each stereotype object has a constructor property pointing to the constructor itself

5. Instance **proto** points to the same place as the prototype object (constructor prototype)

6.Object.prototype.proto===null

7.Function.proto === Function.prototype

8.Object created by Function: Object.proto === Function.prototype

Classical prototype chain diagram analysis

function Foo() let f1 = new Foo(); let f2 = new Foo(); f1.__proto__ = Foo.prototype; // The instance's __proto__ refers to the constructor's prototype f2.__proto__ = foo.prototype; // The instance's __proto__ refers to the constructor's prototype foo.prototype. __proto__ = object.prototype; / / (Foo prototype essence is also a common object, __proto__ instance pointing in the direction of the prototype of the constructor) Foo prototype. The constructor = Foo; Foo.__proto__ = Function. Prototype; Foo.__proto__ = Function. // The instance's __proto__ refers to the constructor's prototype function.prototype. __proto__ = object.prototype; //(Function. Prototype is essentially a normal Object, and its __proto__ refers to the constructor's prototype) object.prototype. __proto__ = null; Function Object() let o1 = new Object(); // Let o1 = new Object(); let o2 = new Object(); o1.__proto__ = Object.prototype; // The instance's __proto__ refers to the constructor's prototype o2.__proto__ = object.prototype; / / __proto__ instance pointing in the direction of the prototype of the constructor Object. The prototype. The constructor = Object; // object.__proto__ = function. prototype // // The new Object constructor) Function. The prototype. The constructor = Function; Function.__proto__ = Function. Prototype // Instance __proto__ points to the prototype of the constructor (?? Doubtful).Copy the code
  • In addition to Object’s prototype (object.prototype) __proto__ pointing to null, the prototype objects of other built-in function objects (e.g. Array.prototype) and the __proto__ of the custom constructor point to Object.prototype because the prototype Object itself is a normal Object.
Array.prototype.__proto__ = Object.prototype;
Foo.prototype.__proto__ = Object.prototype;
Copy the code

inheritance

1. Call, apply borrow constructor

  • How it works: Inside the subtype constructor, the supertype constructor is called
  • Also called forged object or classical inheritance
  • When creating an instance of a subclass, execute the parent class as if it were a normal function, change this in the function to the instance of the current subclass (use call to modify this), and the private properties and methods in the body of the parent class such as this. XXX = XXX are taken over by the instance of the subclass
Advantage: Pass parameters

Borrowing constructors has one big advantage over prototype chains: subtype constructors can pass arguments to supertype constructors.

Disadvantages: Only instances of subclasses can inherit attributes and methods that are private to the parent class.
function SuperType(name){ this.colors = ["red", "blue", "green"]; This. Name = name} function SubType(){// SuperType supertype. call(this, 'Jack'); } var instance1 = new SubType(); this.age = 18; instance1.colors.push("black"); console.log(instance1.colors); //"red,blue,green,black" var instance2 = new SubType(); console.log(instance2.colors); //"red,blue,green"Copy the code

2. Prototype chain inheritance

The basic idea is to use stereotypes to make one reference type inherit the properties and methods of another

  • Principle: Make the prototype of a subclass equal to an instance of the parent class (instances of the parent class can have private and public attribute methods of the parent class), so that instances of a subclass can have both private and public attributes of the parent class.

  • Private attributes and public attribute methods of the parent class are inherited by subclasses and become public attributes and methods of the subclass instance

  • But prototypal inheritance and genetic is different, is the parents’ genes cloning a to yourself (call inherited genetic), and the prototype inheritance is only is for parents and children is established between prototype chain link channel, the public methods of the parent subclass instance use, still on the prototype of the parent class, when using only found via the prototype chain

  • Each constructor has a stereotype object, each stereotype object contains a pointer to the constructor, and each instance contains an internal pointer to the stereotype

  • The instance points to the constructor’s prototype object through __proto__, and the prototype object __proto__ points to the superclass’s prototype object

function SuperType(){ this.property = true; } SuperType.prototype.getSuperValue = function(){ return this.property; }; function SubType(){ this.subproperty = false; } // SuperType subtype.prototype = new SuperType(); SubType.prototype.getSubValue = function (){ return this.subproperty; }; var instance = new SubType(); console.log(instance.getSuperValue()); //true console.log(instance.__proto__ === SubType.prototype) // true console.log(SubType.prototype.__proto__ === SuperType.prototype) // trueCopy the code

Instance __proto__ points to SubType’s prototype, and SubType’s __proto__ points to SuperType’s prototype, which eventually points to Object.prototype

Note, however, that instance.constructor points to SuperType

The ultimate graphic

  • The instanceOf operator detects the relationship between instances and stereotypes
instance instanceof Object  //true 
instance instanceof SuperType  //true 
instance instanceof SubType)  //true
Copy the code
  • The isPrototypeOf() method checks the relationship between an instance and a stereotype
Object.prototype.isPrototypeOf(instance) //true 
SuperType.prototype.isPrototypeOf(instance) //true 
SubType.prototype.isPrototypeOf(instance) //true 
Copy the code
  • Note that you cannot create the stereotype method using literals, as this is equivalent to rewriting the stereotype and severing the link between the constructor and the stereotype
Disadvantages:
  • 1. Whether the parent class is private or public, the subclass is public
  • 2. Share reference values
  • 3. Cannot pass arguments to the supertype constructor when creating subtype instances
function Parent() { this.x = 100; } Parent.prototype.getX = function () { console.log(++this.x); }; function Child() { this.y = 200; } Child.prototype = new Parent(); / / - > written in the first step, the follow-up to subclass the prototype to add some of their own properties and methods (to prevent the method from the original attributes). / / the console log (Child) prototype) constructor); / / Parent constructor to the constructor Child. The prototype. The constructor = Child; Function () {console.log(--this.y); }; var c = new Child(); console.log(c);Copy the code
Function Person(){this.name = 'jack' this.subs = [' English ', 'basketball', 'tennis']; } function Sub() { } Sub.prototype = new Person() var p1 = new Sub() var p2 = new Sub() p1.subs = ['chinese'] console.log( p1.subs); // ['chinese'] console.log( p2.subs); // [' English ', 'basketball', 'tennis'] p1.name = 'Ann' // Basic type p1.subs.push(' Chinese ') // Reference type console.log(p1.name, 'tennis') p1.subs); // Ann [ 'english', 'basketball', 'tennis', 'chinese' ] console.log(p2.name, p2.subs); // jack [ 'english', 'basketball', 'tennis', 'chinese' ]Copy the code

3. Combinatorial inheritance

  • Also called pseudo-classical inheritance
  • Idea: Inheritance of attributes and methods is realized using prototype chains, and inheritance of instance attributes is realized using constructors. This enables reuse of functions by defining methods on prototypes and ensures that each instance has its own attributes
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); 
    this.age = age; 
} 
// 继承方法
SubType.prototype = new SuperType(); 
SubType.prototype.constructor = SubType; 
SubType.prototype.sayAge = function(){ 
    console.log(this.age); 
}; 
var instance1 = new SubType("Nicholas", 29); 
instance1.colors.push("black"); 
console.log(instance1.colors); //"red,blue,green,black" 
instance1.sayName(); //"Nicholas"; 
instance1.sayAge(); //29 
var instance2 = new SubType("Greg", 27); 
console.log(instance2.colors); //"red,blue,green" 
instance2.sayName(); //"Greg"; 
instance2.sayAge(); //27
Copy the code
  • The instanceof operator and isPrototypeOf() methods can also detect objects created based on inheritance

4. Original type inheritance

  • There is no strict constructor for this method.
  • The idea is that stereotypes allow you to create new objects based on existing objects without having to create custom types
function object(o){ 
    function F(){} 
    F.prototype = o; 
    return new F(); 
}
Copy the code
var person = {
    name: "Nicholas", 
    friends: ["Shelby", "Court", "Van"] 
}; 
var anotherPerson = object(person); 
anotherPerson.name = "Greg"; 
anotherPerson.friends.push("Rob"); 
var yetAnotherPerson = object(person); 
yetAnotherPerson.name = "Linda"; 
yetAnotherPerson.friends.push("Barbie"); 
alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
Copy the code

The object.create () and Object () methods behave the same

ES5 has added the object.create (optional Object for new Object prototypes, optional Object for new Object definitions of additional properties) method to standardize old-style inheritance.

var 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"

var anotherPerson1 = Object.create(person, { 
    name: { 
        value: "Greg" 
    } 
}); 
alert(anotherPerson1.name); //"Greg"
Copy the code

Parasitic inheritance

  • Idea: Create a function that only encapsulates the inheritance process and internally enhances the object in some way
var clone = object(original); SayHi = function(){// Somehow enhance this object alert("hi"); }; return clone; Var person = {name: "Nicholas", friends: ["Shelby", "Court", "Van"]}; var anotherPerson = createAnother(person); anotherPerson.sayHi(); //"hi"Copy the code

Parasitic inheritance can also be useful in cases where the main consideration is not custom types and constructors. This pattern applies to any function that can return a new object.

Parasitic combinatorial inheritance

  • Idea: Inherit properties by borrowing constructors, and inherit methods through a mashup of prototype chains.
  • Basic idea: Instead of calling the constructor of a supertype to specify the stereotype of a subtype, all you need is a copy of the stereotype of the supertype
  • Essentially, you use parasitic inheritance to inherit the stereotype of the supertype, and then assign the result to the stereotype of the subtype
Function inheritPrototype(subType, subType) superType){ var prototype = object(superType.prototype); Prototype. constructor = subType; // specify object subType. Prototype = prototype; // Enhance object}Copy the code
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); 
    this.age = age; 
} 
inheritPrototype(SubType, SuperType); 
SubType.prototype.sayAge = function(){ 
    console.log(this.age); 
};
Copy the code

conclusion

  • Factory pattern: Use simple functions to create objects, add properties and methods to them, and then return objects. This pattern was eventually replaced by constructors

  • Constructors: You can create custom reference types, using the new operator just as you would create built-in object instances. Disadvantages: Every member cannot be reused, including functions.

  • Stereotype pattern: Use the constructor’s Prototype property to specify shared properties and methods. When using a combination of constructor and stereotype patterns, you use constructors to define instance properties and stereotypes to define shared properties and methods

  • JavaScript implements inheritance primarily through prototype chains. A chain of stereotypes is built by assigning an instance of a type to a stereotype of another constructor. In this way, the subtype has access to all the properties and methods of the supertype, much like class-based inheritance.

  • The problem with stereotype chains is that all object instances share properties and methods. The solution to this problem is to borrow the constructor, that is, to call the supertype constructor inside the subtype constructor. This ensures that each instance has its own attributes and that only the constructor pattern is used to define types.

  • The most commonly used is composite inheritance: using stereotype chains to share properties and methods, and inheriting instance properties by borrowing constructors

  • Old-style inheritance: Inheritance can be implemented without having to define constructors beforehand. The essence is to perform a shallow copy of the specified object. The resulting copy can be further modified.

  • Parasitic inheritance: Very similar to prototypical inheritance. Create an object based on an object or some information, enhance an object, return an object. To address the inefficiency caused by multiple calls to supertype constructors, the composite inheritance pattern can be used together with composite inheritance.

  • Combining the advantages of both parasitic inheritance and composite inheritance is the most effective way to implement type-based inheritance