JavaScript中的继承方式

353 阅读2分钟

JavaScript中的继承方式

1.原型继承

最简单的继承方式,实现时主要需要搞清楚prototype与对象原型[[prototype]]的关系

简单点说就是:

[[prototype]]是对象里的一个隐藏属性,指向对象的上一级原型,使用__proto__可以获取到这个属性,在现代js中,也可以使用Object.getPrototypeOf()函数来获取对象的原型。

prototype是构造函数中的一个属性,它是一个对象,里面有一个constructor属性指向这个构造函数,当使用构造函数构造对象时,构造的这个对象的[[prototype]]属性指向这个构造函数的prototype属性。

示意图:

image-20220326171927030.png 图片来源:zh.javascript.info/function-pr…

    function Animal(){
        this.name = 'animal';
        console.log("zhixingle");
    }

    Animal.prototype.getName = function(){
        return this.name;
    }

    function Dog() {};
    Dog.prototype = new Animal();
	console.dir(dog.name) //'animal'

原型继承的存在的问题:

​ 无法向父类传递参数

2.借用构造函数实现继承

        function Animal(name,color){
            this.name  = name;
            this.color = color;
            this.getName = ()=>this.name;
        }

        function Dog(name,color,price){
            Animal.call(this,name,color);
            this.price = price;
        }

        function Cat(name,color,price){
            Animal.call(this,name,color);
            this.price = price;
        }

        let dog = new Dog('小羊',['white','black'],'3000$');
        let cat = new Cat('圆圆',['grey','yellow'],'2000$');
        console.log(dog.getName===cat.getName);//**false

这个方法解决了原型继承不能给父类传递参数的不足,但是它也有自己的不足:

​ 1.不能继承原型的属性,这个方法本质上还是将属性值付给了自己

​ 2.不能实现函数复用,每次都需要创建相同功能的函数,造成不必要的浪费。如上方的代码中:console.log(dog.getName===cat.getName);//**false

3.组合继承

        function Animal(name,color){
            this.name  = name;
            this.color = color;
        }

        Animal.prototype.getName = ()=>this.name;

        function Dog(name,color,price){
            Animal.call(this,name,color);
            this.price = price;
        }

        function Cat(name,color,price){
            Animal.call(this,name,color);
            this.price = price;
        }

        Dog.prototype = new Animal();

        let dog = new Dog('小羊',['white','black'],'3000$');
        console.log(dog);
        let cat = new Cat('圆圆',['grey','yellow'],'2000$');
        console.log(cat);

        console.log(cat.getName===dog.getName);//true

这个方法结合了前面两种实现方法,结合了前面两种方法的优点,同时解决了函数不能复用的缺陷。

当然它也有缺点:

​ 重复运行父类的实例,为原型添加了不必要的属性.

4.寄生组合继承

function inherit(parent, child){
  const Super = function(){};
  Super.prototype = parent.prototype;
  child.prototype = new Super();
  //修正constructor属性
  child.prototype.constructor = child;
}

// 父类
function Animal(name, color){
  this.name  = name;
  this.color = color;
}
Animal.prototype.greet = function() {
  console.log(`hello my name is ${this.name}`)
}

// 子类
function Dog(name,color,price){
  Animal.call(this, name, color);
  this.price = price;
}

// 子类
function Cat(name, color, price){
  Animal.call(this, name, color);
  this.price = price;
}
inherit(Animal, Cat)
inherit(Animal, Dog)

const cat = new Cat('tome', 'orange', '1000CNY')


cat.greet()

解决了前三种的所有问题

5.使用es6的class实现继承

        class Animal{
            constructor(name='animal',color){
                this.name  = name;
                this.color = color;
                console.log("父类初始化");
            }

            getName(){
                return this.name;
            }
        }

        class Dog extends Animal{
            constructor(name,color,price){
                super(name,color);
                console.log(this.name);
                this.price = price;
            }
        }

        class Cat extends Animal{
            constructor(name,color,price){
                super(name,color);
                this.price = price;
            }
        }


        let dog = new Dog('小羊',['white','black'],'3000$');
        console.log(dog);
        let cat = new Cat('圆圆',['grey','yellow'],'2000$');
        console.log(cat);

        console.log(cat.getName===dog.getName);

参考文章:

1.zh.javascript.info/prototype-i…

2.zhuanlan.zhihu.com/p/37735247

3.juejin.cn/post/694602…