JS对象继承模式

728 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第23天,点击查看活动详情

JS对象的继承模式

之前的文章写过JS对象,也简述原型

原型链继承

原理:
定义父类型构造函数
给父类型的原型添加方法
定义子类型的构造函数
创建父类型的对象赋值给子类型的原型
将子类型原型的构造属性设置为子类型
给子类型原型添加方法
创建子类型的对象:可以调用父类型的方法

关键:
子类型的原型为父类型的一个实例对象

示例:

定义“父类型”

    function Supper() {
            this.supProp = 'Supper property';
        }

往Supper的原型对象中添加方法

    Supper.prototype.showSupperProp = function () {
            console.log(this.supProp);
        }

然后定义“子类型”

    function Sub() {
            this.subProp = 'Sub property';
        }

创建一个Sub的实例,去调用Supper原型对象中的方法showSupperProp,以及调用toString方法

        var sub = new Sub();
        sub.showSupperProp()
        sub.toString();

显然会报错,因为在sub的这个实例中以及原型链上没有找到这个方法。当子类的原型对象为父类型的一个原型对象时,就不会报错了
而toString能找到,原因是 sub.__proto__指向了Sub.prototype,而Sub的原型对象是一个空Object实例对象(相当于 Sub.prototype = {} ),这个对象有隐式原型属性,指向了Object.prototype,toString就是定义在Object的原型对象上的。

.

上面是因为没有继承到“父类型”,所以直接调用别人原型对象中的方法是不行的,所以接下来要想实现继承,关键就在于 ==> 子类型的原型为父类型的一个实例对象

    Sub.prototype = new Supper();

执行完上面的代码,刚刚那个报错的地方就不会报错了。

但是存在一个问题,来看看

    console.log(sub.constructor); // f Supper()

其实这里有个问题,我们利用constructor去查看sub的构造函数的时候返回的是Supper。
sub本身没有constructor属性,因为Sub的原型对象是Supper的一个实例
(这个实例的隐式原型属性的值和它的构造函数的显式原型的值是一样的)

所以constructor属性存在于Supper的原型对象中
(Supper的prototype指向了Supper的原型对象,Supper的原型对象中的constructor指回了Supper这个构造函数,这是相互引用的关系)

当sub调用constructor的时候,就会顺着原型链一步步往上,一直到了Supper的原型对象找到了constructor,最后返回constructor所指向的值,也就是Supper构造函数

那这样不行啊,我是Sub的实例,这个构造器属性居然指向了Supper,这样不太好,所以需要多一步操作,给Sub的原型对象中追加一个constructor属性,让他指向Sub构造函数
也就是在Sub.prototype = new Supper();之后马上就要写:

    Sub.prototype.constructor = Sub;

以上是利用原线链继承的一个过程。
下面是完整代码以及原理图

image.png

借用构造函数继承

(我认为其实没有什么继承,就是简化代码而已,借用别人的构造函数来初始化我的数据而已,和python那个借用别人的构造函数来初始化自己的属性那个是差不多的0 0)

原理:
定义父类型构造函数
定义子类型构造函数
在子类型构造函数中调用父类型构造函数

关键:
在子类型构造函数中用call()调用父类型构造函数

示例:

        function Person(name, age) {
            this.name = name;
            this.age = age;
        }

        function Student(name, age, price) {
            Person.call(this, name, age)
            this.price = price;
        }

        var s = new Student('Tom', 20, 14000);
        console.log(s.name, s.age, s.price);// Tom 20 14000

上面那个call方法那里
相当于 this.Person(name,age),但我不能真的这么写,因为我这里面是没有Person这个函数或者说方法的,我只能去借用一下。让Person这个函数成为this(我new出来的对象)的方法,利用Person来初始化name和age,相当于做了以下几件事:

                this.name = name;
                this.age = age;

组合继承

即原型链 + 借用构造函数的组合继承

流程:
1.利用原型链实现对父类型对象的方法继承
2.利用call()借用父类型构造函数初始化相同的属性

案例:

    function Person(name, age) {
            this.name = name;
            this.age = age;
        }
        //比如Person也有一些方法,我们把方法写到Person的原型对象上
        Person.prototype.setName = function (name) {
            this.name = name;
        }

        function Student(name, age, price) {
            Person.call(this, name, age)
            this.price = price;
        }

        //如果要真正产生继承关系,要写两条语句
        //父类型的实例赋值给子类型的原型,为了能看到父类型的方法
        Student.prototype = new Person();
        //给子类型的原型追加构造器constructor指向自己的构造函数
        Student.prototype.constructor = Student;
        Student.prototype.setPrice = function (price) {
            this.price = price;
        }

        var s = new Student('Tom', 24, 15000);
        s.setName('Bob');
        s.setPrice(16000);
        console.log(s.name, s.age, s.price);