In the process of program development, we often use the prototype chain, but, we are not familiar with its concept, recently re-read it, is to enhance memory, incidentally record, hope to help everyone!

The foundation of the prototype chain

Basic idea: A stereotype chain uses stereotypes to make one reference type inherit the properties and methods of another reference type.

Explain the relationship between constructors, prototypes, and instances

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 object.

Implement the basic pattern of the prototype chain


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

  function SubType() {
    this.subproperty = false
  }

  SubType.prototype = new SuperType()
  // SuperType is inherited
  SubType.prototype.getSubValue = function () {
    return this.subproperty
  }

  var instance = new SubType()

console.log(instance.getSuperValue()) // true
Copy the code

They inherit by using subtype.prototype = new SuperType(), creating an instance and assigning the instance to prototype

There are two things here,

  1. The first is to make the default prototype of all functions an instance of object
  2. The second is to determine that the relationship between the prototype and the instance can be determined by instanceof
  3. You cannot use object literals to create prototype methods when you implement inheritance through a prototype chain

The prototype chain problem

    function SuperType() {
        this.color = ['red'.'blue'.'green']}function SubType() {}

    // SuperType is inherited
    SubType.prototype = new SuperType()

    var instance1 = new SubType()
    instance1.color.push('black')
    console.log(instance1.color) // ["red", "blue", "green", "black"]

    var instance2 = new SubType()
    console.log(instance2.color) // ["red", "blue", "green", "black"]
Copy the code

Amazingly, the above code only executes instance1.color.push(‘black’), but in the newly created instance instance2, the output is the same. This is mainly because stereotype properties that contain reference type values are shared by all instances. So there are several ways to solve these problems

2. Borrowing constructors (fake objects or classical inheritance)

The basic idea behind this technique is to call the superclass constructor inside the subclass constructor. The constructor is executed on the newly created object by using the Apply or call methods.

The code is as follows:

    function SuperType() {
        this.colors = ['red'.'blue'.'green']}function SubType() {
        // SuperType is inherited
        SuperType.call(this)}var instance1 = new SubType()
    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

The SuperType constructor is called in the context of a newly created SubType instance, primarily through the use of call.

1. Pass parameters

You can pass arguments to a supertype constructor in a subclass constructor.

    function SuperType(name) {
        this.name = name
    }

    function SubType() {
        // SuperType is inherited, and arguments are passed
        SuperType.call(this.'neil')
            // Instance properties
        this.age = 25
    }

    var instance = new SubType()
    console.log(instance) // SubType {name: "neil", age: 25}
Copy the code

Since methods are defined in constructors, function reuse is a useful problem, hence composite inheritance

Combination inheritance

Composite inheritance, also known as pseudo-classical inheritance, is an inheritance pattern that combines the archetypal chain with the technique of borrowing constructors to exploit the best of both. The idea behind this is to use the stereotype chain for inheritance of stereotype properties and methods, and to borrow constructors for inheritance of instance properties

    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)
        this.age = age
    }

    // Inherit the method
    SubType.prototype = new SuperType()
    SubType.prototype.constructor = SubType
    SubType.prototype.sayAge = function() {
        console.log(this.age)
    }

    let instance1 = new SubType('neil'.25)
    instance1.colors.push('black')
    console.log(JSON.stringify(instance1.colors)) // ["red","blue","green","black"]
    instance1.sayName() // neil
    instance1.sayAge() / / 25

    let instance2 = new SubType('neil2'.23)
    console.log(JSON.stringify(instance2.colors)) // ["red","blue","green"] 
    instance2.sayName() // neil2
    instance2.sayAge() / / 23
Copy the code

The three main steps,

  1. SuperType. Call (this name),
  2. Subtype.prototype = new SuperType(),
  3. SubType. The prototype. The constructor = SubType.

Iv. Inheritance of the original type

In 2006, Douglas Crockford wrote an article called Prototypal Inheritance in JavaScript. In this article he introduces this approach to inheritance. This approach does not use constructors in the strict sense. The idea is that prototypes allow you to create objects based on existing objects without having to create custom types.

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

    let person = {
        name: 'neil'.friends: ['neil1'.'neil2'.'neil3']}let anotherPerson = Object(person)
    anotherPerson.name = 'neil4'
    anotherPerson.friends.push('neil5')

    let yetAotherPerson = object(person)
    yetAotherPerson.name = 'neil6'
    yetAotherPerson.friends.push('neil7')
    console.log(person.friends) // ["neil1", "neil2", "neil3", "neil5", "neil7"]
Copy the code

ECMAScript5 standardizing prototype inheritance by adding the object.create () method. This method takes two parameters: an object that is used as a prototype for the new object and an object that defines additional properties for the new object

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

    let person = {
        name: 'neil'.friends: ['neil1'.'neil2'.'neil3']}let anotherPerson = Object.create(person)
    anotherPerson.name = 'neil4'
    anotherPerson.friends.push('neil5')

    let yetAotherPerson = Object.create(person)
    yetAotherPerson.name = 'neil6'
    yetAotherPerson.friends.push('neil7')

    console.log(person.friends) // ["neil1", "neil2", "neil3", "neil5", "neil7"]
Copy the code

The difference is that one is similar to a factory function that returns an instance, and the other is implemented via object.create.

Fifth, parasitic inheritance

The idea of parasitic inheritance is similar to the parasitic constructor and factory pattern, creating a function that only encapsulates the inheritance process, internally enhances the object in some way, and then returns the object.

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

    let person = {
        name: 'neil'.friends: ['neil1'.'neil2'.'neil3']}function createAnother(originnal) {
        let clone = Object(originnal)
        clone.sayHi = function() {
            console.log('hi')}return clone
    }

    let anotherPerson = createAnother(person)
    anotherPerson.sayHi() // hi
Copy the code

Not only does the new object have all the properties and methods of person, but it also has its own sayhi() method.

Related articles:

Factory patterns, constructor patterns, and prototype patterns