整理面向对象的继承

阶段一

首先创建一个父类的函数方便继承

function Animal(aniamlType,age){
            this.aniamlType = aniamlType
            this.age = age
        }

然后再创建一个子类用来继承

function Dog(aniamlType, age, name) {
            Animal.call(this, aniamlType, age)
            this.name = name
        }

只需将this传进去执行一遍Animal函数,就能得到所有的属性了。
但是父类prototype的方法并不会被继承下来!

function Animal(aniamlType, age) {
        this.aniamlType = aniamlType
        this.age = age
    }
    Animal.prototype.eat = function () {
        console.log('eat')
    }
    function Dog(aniamlType, age, name) {
        Animal.call(this, aniamlType, age)
        this.name = name
    }
    var dog1 = new Dog('dog', '18', 'wb')
    dog1.eat()//Uncaught TypeError: dog1.eat is not a function

阶段二

由于父类的方法没办法继承,所以这时候我们就考虑从子类的prototype上来选择继承。这样就能让实例proto指向子类的prototype的时候能够得到父类的方法和属性了。
这时候我们将代码改一改

        function Animal(aniamlType, age) {
            this.play = ['eat','bite','shout']
        }
        Animal.prototype.eat = function () {
            console.log('eat')
        }
        function Dog(aniamlType, age, name) {
            this.aniamlType = aniamlType
            this.age = age
            this.name = name
        }
        Dog.prototype = new Animal()
        var dog1 = new Dog('dog', '18', 'wb')
        dog1.eat() //eat

可以看到这样就能让实例得到父类的方法了。
但是当我再创建一个实例的时候,然后修改父类的时候,由于两个实例的proto是子类的prototype,而子类的prototype引用同一个地址,即父类的实例(new Animal()),所以修改一个实例里面来自父类的对象的时候,另一个实例的那个对象也会跟着发生改变

        var dog1 = new Dog('dog', '18', 'wb')
        var dog2 = new Dog('dog', '20', 'wbd')
        dog1.play.push('owwwww')
        console.log(dog1.play)//["eat", "bite", "shout", "owwwww"]
        console.log(dog2.play)//["eat", "bite", "shout", "owwwww"]

这种方法虽然可以继承父类的方法,但是改变父类对象的时候,其他实例的这个对象也会跟着改变。

阶段三

上述两个阶段都有其可取之处,也有缺点。所以我们可以想到,应该能两者一起混合使用。于是就有了第三种方案了。

        function Animal(aniamlType, age) {
            this.aniamlType = aniamlType
            this.age = age
            this.play = ['eat', 'bite', 'shout']
        }
        Animal.prototype.eat = function () {
            console.log('eat')
        }
        function Dog(aniamlType, age, name) {
            Animal.call(this, aniamlType, age, name)//使用阶段一来继承属性
            this.name = name
        }
        Dog.prototype = new Animal()//使用阶段二来继承方法
        var dog1 = new Dog('dog', '18', 'wb')
        var dog2 = new Dog('dog', '20', 'wbd')
        dog1.play.push('owwwww')
        console.log(dog1.play)//["eat", "bite", "shout", "owwwww"]
        console.log(dog2.play)//["eat", "bite", "shout"]

这时候更改一个实例的时候就不会出现阶段三的问题了。
但是子类new,实例也要new一下,会增加性能损耗
于是想到直接等于让子类的prototype = 父类的prototype好了

        Dog.prototype = Animal.prototype

但是这时候问题又来了,子类在prototype上添加的方法会直接影响到父类的prototype,不方便别的子类继承该父类。而且子类的constructor会发生改变。

        Dog.prototype.cat = function () {
            console.log('cat')
        }
        console.log(Animal.prototype.cat) // function () { console('cat') }
        console.log(dog1.constructor)//function Animal ....

在上面会发现创建实例的竟然是爷爷,而不是爸爸。这就乱套了。
所以这时候就需要将子类的prototype复制父类的prototype而不是直接引用了。这时候不仅父类没有了方法,而且还可以直接修改子类的constructor了

        Dog.prototype = Object.create(Animal.prototype)
        Dog.prototype.cat = function () {
            console.log('cat')
        }
        Dog.prototype.constructor = Dog
        console.log(Animal.prototype.cat) //undefined
        console.log(dog1.constructor) //function Dog

这时候才是完美的面向对象的继承。
总结:
1.在子类内部继承属性
2.在子类的prototype上复制父类的prototype
3.更改子类prototype.constructor,让其指向自己。


更简单方便的方法,使用ES6的Class继承

        class Animal {
            constructor(aniamlType, age) {
                this.aniamlType = aniamlType
                this.age = age
            }
            eat() {
                console.log('eat')
            }
        }
        class Dog extends Animal {
            constructor(aniamlType, age, name) {
                super(aniamlType, age)
                this.name = name
            }
            cat() {
                console.log('cat')
            }
        }
        var dog1 = new Dog('dog', '18', 'wb')
        console.log(dog1.aniamlType, dog1.age, dog1.name)//dog 18 wb
        dog1.cat()//cat
        dog1.eat()//eat
        console.log(dog1.constructor) //class Dog extends Animal {...}

可以看出ES6的class不用那么复杂了。直接使用,没有后顾之忧

你可能感兴趣的:(整理面向对象的继承)