继承

107 阅读4分钟

OIP-C.jfif

es5中函数的继承

在es5中实现继承的方法有:
1、原型链继承
2、借助构造函数继承
3、组合式继承
4、原型式继承
5、寄生式继承
6、寄生式组合继承

1、原型链继承:顾名思义就是依靠原型链实现继承,关键点在于让父类构造函数创建的实例赋值给子类的原型,这样子类的实例通过原型链可以访问到父类的原型(可以使用父类原型上的方法),除此之外因为父类的实例赋值给了子类的原型,所以父类构造函数中的this也就是子类原型,子类的原型上就有了父类构造函数的实例属性,进一步子类的实例可以通过原型链访问到父类构造函数的实例属性。

    function Parent(name){
            this.name = name;
            this.age = 18
        }
        Parent.prototype.sayhi = function(){
            return 'hi'
        }
        function Son(){}
        Son.prototype = new Parent('sari')
        let son1 = new Son();
        let son2 = new Son();
        console.log(son1.name)
        console.log(son2.name)
        console.log(son1.sayhi())
        console.log(son2.sayhi())

缺点:虽然通过这种方法,子类的实例不仅可以访问到父类原型上的方法,还可以访问到父类的实例属性。但是无法在不改变所有子类实例的情况下给父类构造函数传递参数。

2、借助构造函数继承:这个方法其实是改变父类的this指向,让他的this指向子类的实例,那么子类实例就能够使用父类的实例属性。这样每次创建子类实例的时候都会相应的创建一次父类的构造函数,因为有下面的Parent.call(this,args)。就解决了方法1中的缺点。

function Parent(name){
            this.name = name;
            this.age = 18
        }
function Son(args){
            Parent.call(this,args)
        }
let son1 = new Son('hello')
console.log(son1.name)

3、组合式继承:这个方法是取了1、2方法的优点,用原型链继承的方式实现对父类原型方法的继承,利用借助构造函数的方法实现对实例属性的继承

function Parent(name){
            this.name = name;
            this.age = 18;
        }
        Parent.prototype.sayhi= function(){
            return 'hi'
        }
        function Son(args){
            Parent.call(this,args)
        };
        Son.prototype = new Parent();
        let son1 = new Son('tom')
        let son2 = new Son('xiaoming')
        console.log(son1.name)
        console.log(son2.name)
        console.log(son1.sayhi())
        console.log(son2.sayhi())
        //tom xiaoming hi hi

4、原型式继承:原型原型,说白了也是用到了原型链的东西,其核心就是在封装的函数中的 F.prototype = o;这个封装的object函数,返回的是F的实例,让这个实例的构造函数的原型指向参数o,这样就可以继承o的属性

let obj = {
            name:'tom',
            age:18
        }
        function object(o){
            let F = function(){}
            F.prototype = o
            return new F
        }
        
        let o1 = object(obj)
        console.log(o1.name)
        console.log(o1.age)
        // tom 18

后来这个方法演化成了Object.create(o)的方法,就是创建一个实例对象,这个实例对象 instanceof o;也就是生成的实例可以通过原型链访问到o的属性。

 let obj = {
            name:'tom',
            age:18
        }
        let p = Object.create(obj);
        console.log(p.name);
        console.log(p.age)
    //  tom 18

5、寄生式继承:寄生式继承就是在原型式继承的基础上进行相应的添加,如下:可以添加一个方法等。

  let obj = {
            name:'tom',
            age:18
        }
        function object(o){
            let F = function(){}
            F.prototype = o
            return new F
        }
        function createPerson(o){
          let p = object(o)
          p.sayhi = function(){
              return 'hi'
          }
          return p
        }
        let p1 = new createPerson(obj)
        console.log(p1.name)
        console.log(p1.sayhi())
        // tom hi

6、寄生式组合继承:虽然组合式继承已经算是比较完美,但是还是有一定的缺点,比如,每次继承都会调用两次父类构造函数,在Son.prototype = new Parent()和Parent.call(this,args)时调用.所以需要使用寄生式组合继承。他的真正的核心是: let prototype = object(Parent.prototype),其他部分跟组合式继承没什么区别。

function object(o){
            let F = function(){}
            F.prototype = o
            return new F
        }
        function Parent(name){
            this.name = name;
            this.age = 18
        }
        Parent.prototype.sayhi=function(){
            return 'hi'
        }
        function Son(args){
            Parent.call(this,args)
        }
        let prototype = object(Parent.prototype)
        Son.prototype = prototype
        let son1 = new Son('tom')
        console.log(son1.name)
        console.log(son1.sayhi())
        // tom hi

es6的class和extends继承

class类其实是一个语法糖,他的本质还是一个函数

 class Person{}
 console.log(typeof(Person))
 //function

class分成三个部分,构造器函数部分,原型方法部分,静态方法部分

 class Person{
        constructor(name,age){
            this.name = name;
            this.age = age 
        }
        sayhi(){
            return 'hi'
        }

        static eat(){
            return 'eat somthing'
        }
    }
    let p = new Person('tom',19)
    console.log(p.name)
    console.log(p.age)
    console.log(p.sayhi())
    // tom 19 hi

从上面可以看出构造器函数对应着实例属性,原型方法部分(sayhi)是定义在原型上的方法,静态方法(static eat)是属于类自身的方法。 通过extends实现继承

class Parent{
        constructor(name,age){
            this.name = name;
            this.age = age 
        }
        sayhi(){
            return 'hi'
        }

        static eat(){
            return 'eat somthing'
        }
    }

    class Son extends Parent{}
    let son1 = new Son('tom')
    console.log(Son.eat())
    console.log(son1.name)
    console.log(son1.sayhi()) 
    // eat somthing; tom; hi 

可以看出子类完美继承了父类的实例属性,原型方法,静态方法 继承原型方法的原理是:Son.prototype.proto = Parent.prototype

继承静态方法的原理是:Son.proto = Parent

继承实例属性则依靠super(),super()会调用父类的构造函数,将返回的实例指向this。super()调用构造函数时如果需要传参,则手动传入。super(args)其实相当于Parent.constructor.call(this,args)