js inheritance method - prototype chain, stealing constructor, combination inheritance, prototype inheritance, parasitic inheritance, parasitic combination inheritance

inherit

Many object-oriented languages ​​support two types of inheritance: interface inheritance and implementation inheritance. The former only inherits the method signature, the latter inherits the actual method. Interface inheritance is not possible in ECMAScript because functions do not have signatures. Implementation inheritance is the only type of inheritance supported by ECMAScript, and this is achieved primarily through the prototype chain. Inheritance has the following six implementation methods: prototype chain, stealing constructor, combination inheritance, prototype inheritance, parasitic inheritance, parasitic combination inheritance

Bibliography "JavaScript Advanced Programming (4th Edition)"

1. Prototype chain

Prototype Chain Inheritance

Before that, it is necessary to clarify the concept of prototype and prototype chain. Prototype and prototype chain - Question Mark Manufacturer's Blog - CSDN Blog

Taking the following code as an example, the key to inheritance is that Son does not use the default prototype, but replaces it with a new object (Son.prototype=new Father())

Prototype chain inheritance code

/**原型链继承代码模式**/
function Father(){
    this.lastName='Zhang'
    this.firstName='baba'
}
Father.prototype.getLastname=function(){
    return this.lastName
}
function Son(){
    this.firstName='San'
}
//Son继承Father
Son.prototype=new Father()
Son.prototype.getName=function(){
    return this.lastName+this.firstName
}
let instance1=new Son()
console.log(instance1.getName()) //ZhangSan

Prototype Chain Inheritance Diagram

Prototype Chain Search Mechanism

When reading an attribute on an instance, it will first search on the instance. If it is not found, it will inherit the prototype of the searched instance. If it has not been found, it will continue to search for the prototype of the prototype.

shortcoming

  • Reference values ​​contained in the prototype are shared across all instances

  • The subtype cannot pass parameters to the constructor of the parent type when inheriting

2. Stealing the constructor

  • Solve the inheritance problem caused by the prototype containing the reference value

  • Solve the problem that the subtype cannot pass parameters to the parent type

The basic idea

Call the parent class constructor in the subclass constructor, use the call method to change the this point, and execute the constructor with the newly created object as the context

	function Father(name){
            this.name=name
            this.colors=['red','green','blue']
        }
        function Son(){
            //继承Father
            Father.call(this,'zhangsan')//谁调用this就指向谁
            this.age=18
        }
        let instance1=new Son()
        instance1.colors.push('pink')
        console.log(instance1.colors,instance1.name) //['red','green','blue','pink'] 'zhangsan'

        let instance2=new Son()
        console.log(instance2.colors) //['red','green','blue']

By using the call method, the Father constructor is executed in the context of the object created for Son, which is equivalent to running all the initialization code in the Father() function on the new Son object, so each instance will have its own name , age and colors properties

shortcoming

  • The method must be defined in the constructor, so the function cannot be reused

  • Subclasses cannot access methods defined on the parent class prototype

3. Combined inheritance

The basic idea

Combined inheritance combines the prototype chain and the stolen constructor, uses the prototype chain to inherit the properties and methods on the prototype, and uses the stolen constructor to inherit the instance properties

	function Father(firstName){
            this.lastName='Zhang'
            this.firstName=firstName
            this.colors=['red','green','blue']
        }
        Father.prototype.getLastname=function(){
            return this.lastName
        }
        function Son(name){
            //盗用构造函数继承Father的属性,子类型向父类型传参
            Father.call(this,name)
        }
        //Son原型链继承Father
        Son.prototype=new Father()
        Son.prototype.getName=function(){
            return this.lastName+this.firstName
        }
        let instance1=new Son('san')
        instance1.colors.push('pink')
        console.log(instance1.getName(),instance1.colors) //Zhangsan ['red','green','blue','pink']

        let instance2=new Son('ha')
        instance1.colors.push('orange')
        console.log(instance2.getName(),instance2.colors) //Zhangha ['red','green','blue','orange']

shortcoming

  • The superclass constructor is always called twice: once when the subclass prototype is created, and once in the subclass constructor

4. Prototype inheritance

  • Do not create constructors separately to share information between objects

Be applicable

When you don't need to create a separate constructor, but still need to share information between objects

The basic idea

The object() function temporarily creates a constructor, assigns the incoming object to the prototype of the constructor, and returns an instance of the temporary type

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

ECMAScript5 added the Object.create() method, which receives two parameters: the object as the prototype of the new object; the object that defines additional properties for the new object (can be omitted). When there is only one parameter, Object.create() has the same effect as the above object()

	function object(o){
            function F(){}
            F.prototype=o
            return new F()
        }
        let person={
            name:'happy',
            friends:['joy','candy','amy']
        }
        let instance1=object(person)
        instance1.name='haha'
        instance1.friends.push('tony')
        console.log(instance1)

        let instance2=Object.create(person)
        instance2.friends.push('tom')
        console.log(instance2)

        console.log(person)

In essence, object() performs a shallow copy of the incoming o, so the reference value contained in the object attribute of o will be shared among related objects, as shown in the above print result

shortcoming

  • Reference values ​​contained in parent object properties are always shared between related objects

5. Parasitic inheritance

Be applicable

Focus on objects, not on types and constructors

The basic idea

Create a function that implements inheritance, enhances the object, and returns the object

		//object函數不是寄生式继承所必须的,任何返回新对象的函数都可以在这里使用
	function object(o){
            function F(){}
            F.prototype=o
            return new F()
        }
        function extendsFather(father){
            let clone=object(father)
            clone.age=20
            clone.sayHi=function(){
                console.log('Hi~'+clone.name)
            }
            return clone
        }
        let person={
            name:'happy',
            friends:['joy','candy','amy']
        }
        let instance1=extendsFather(person)
        instance1.sayHi() //Hi~happy

shortcoming

  • Difficult to reuse functions

6. Parasitic Composition Inheritance

  • Solve the problem that the parent class constructor will always be called twice in combined inheritance

problem analysis

首先简要回顾一下组合继承
function Son(name){
//盗用构造函数继承Father的属性,子类型向父类型传参
Father.call(this,name) //第二次调用Father
}
//Son原型链继承Father
Son.prototype=new Father() //第一次调用Father()
let instance1=new Son('san')
第一次调用Father时,Father的实例属性(lastName、firstName、colors)会成为Son的原型属性,第二次调用Father时,会在新对象上创建Father的实例属性,由于原型链的搜索机制,实例属性会遮蔽原型上的同名属性,这也是为什么 盗用构造函数继承可以解决原型链继承造成的引用值在实例间共享的问题
由此带来的问题是:有两组lastName、firstName、colors属性,一组在实例instance1上(第二次调用Father所致),一组在Son的原型上(第一次调用Father所致)
寄生式组合继承很好的解决了这个问题

基本思路

不通过调用父类构造函数给子类原型赋值(即不使用Son.prototype=new Father()),而是使用寄生式继承来继承父类原型,然后将返回的新对象赋值给子类原型

		//寄生式组合继承
        function Father(firstName){
            this.lastName='Zhang'
            this.firstName=firstName
            this.colors=['red','green','blue']
        }
        Father.prototype.getLastname=function(){
            return this.lastName
        }
        function Son(name){
            Father.call(this,name)
        }
        function object(o){
            function F(){}
            F.prototype=o
            return new F()
        }
        function inheritPrototype(son,father){
            let prototype=object(father.prototype)
            prototype.constructor=son
            son.prototype=prototype
        }
        //Son寄生式继承Father
        inheritPrototype(Son,Father)

        Son.prototype.getName=function(){
            return this.lastName+this.firstName
        }
        let instance1=new Son('san')
        instance1.colors.push('pink')
        console.log(instance1) 

这里只调用了一次Father(),避免了Son.prototype上不必要也用不到的属性,因此效率更高。寄生式组合继承是引用类型继承的最佳模式

Guess you like

Origin blog.csdn.net/sxp19980829/article/details/128818711