首页 理论教育继承方式-零基础JavaScript从入门到精通

继承方式-零基础JavaScript从入门到精通

【摘要】:图12.6.3构造继承采用构造继承的方式避免了在多个子类实例共享父类实例,但也导致子类实例不能继承父类的原型,因此child instanceof Supper等于false。图12.6.4实例继承相比于构造继承,实例继承虽然能够从父类获取原型属性和方法,但产生的问题更加严重,它修改了实例与构造函数的对应关系,导致new Child()返回的是child对象而不是Child的实例。

JavaScript基于原型链继承,在实际使用中除了使用原型链继承外,还有构造继承、实例继承、拷贝继承、组合继承、寄生继承等多种方式。

1.原型继承

12.6.2.html使用原型继承在不同对象之间传递属性和方法,如下所示。

动手写12.6.2

执行12.6.2.html,输出结果到网页,如下图所示。

图12.6.2 原型继承

采用原型继承的方式简单、容易理解,但有以下缺点:

◇ 所有子类共享的Child.prototype是一个父类的实例。

◇ 构造子类实例时,没有向父类构造函数传参,导致父类实例的部分属性不能传递给子类实例。

◇ 无法实现多继承。

2.构造继承

12.6.3.html在构造函数内部调用父类的构造函数,继承父类构造函数内部定义的属性和方法,如下所示。

动手写12.6.3

执行12.6.3.html,输出结果到网页,如下图所示。

图12.6.3 构造继承

采用构造继承的方式避免了在多个子类实例共享父类实例,但也导致子类实例不能继承父类的原型,因此child instanceof Supper等于false。

3.实例继承

12.6.4.html在子类构造函数内部调用父类构造函数,创建父类实例对象,并设置子类构造函数内部定义的属性和方法,实现实例继承,如下所示。

动手写12.6.4

执行12.6.4.html,输出结果到网页,如下图所示。

图12.6.4 实例继承

相比于构造继承,实例继承虽然能够从父类获取原型属性和方法,但产生的问题更加严重,它修改了实例与构造函数的对应关系,导致new Child()返回的是child对象而不是Child的实例。

4.拷贝继承

将“父类”原型上所有的属性和方法赋值(拷贝)给“子类”实例对象,实现继承。

12.6.5.html使用拷贝实现继承,如下所示。

动手写12.6.5

(www.chuimin.cn)

执行12.6.5.html,输出结果到网页,如下图所示。

图12.6.5 拷贝继承

拷贝继承解决了子类实例child访问Child原型属性和方法的问题,但它与实例继承存在同样严重的问题,即修改了构造函数本应该返回的实例,导致child instanceof Child等于false,并且导致了更多的性能开销。

5.组合继承

采用原型继承和构造继承组合的方式,确保子类实例拥有不同的父类实例属性。

12.6.6.html使用组合继承,如下所示。

动手写12.6.6

执行12.6.6.html,输出结果到网页,如下图所示。

图12.6.6 组合继承

组合继承仍然有一个缺点,就是调用两次父类构造函数,在子类实例和Child.prorotype都具有父类的实例属性。访问子类实例属性时会屏蔽Child.prototype上的同名属性,目前这种继承方式较能被接受。

6.寄生组合继承

寄生组合继承就是在子类和父类之间设置空对象,实现继承。

12.6.7.html使用寄生组合继承,如下所示。

动手写12.6.7

执行12.6.7.html,输出结果到网页,如下图所示。

图12.6.7 寄生组合继承

通过中间的空白函数去掉Child.prototype上Supper的实例属性。这种方式并没有消除多余的Supper实例,反而让原型链多了一层。

7.proto继承

在单继承上,上述继承方式都没有完美解决冗余的Supper实例、instanceof不符合预期、构造函数返回其他函数的实例等问题,虽然名称不同但实现方式类似。只有修改Child.prototype的原型,才能解决上述问题。

12.6.8.html修改子类的__proto__属性实现继承,如下所示。

动手写12.6.8

执行12.6.8.html,输出结果到网页,如下图所示。

图12.6.8 proto继承

在Child构造函数中,调用Supper构造函数不是必须的,但需要具备这种能力。