JS 组合继承

简介: JS 组合继承

继承也是面向对象的特性之一,但是在 ES6 版本之前是没有 extends 去实现继承的,我们只能通过 构造函数 和 原型对象 来实现继承,其中分别为构造函数来继承属性,原型对象来继承方法,这种继承模式被称为 组合继承




文章目录:

一:call() 的作用与使用


1.1 使用 call() 来调用函数


1.2 使用 call() 来改变 this 的指向


二:利用构造函数继承父属性


2.1 实现过程


2.1 实现过程分析


三:利用原型对象继承父方法


3.1 继承父方法的错误演示


问题原因


3.2 继承父方法的正确做法


 3.2 继承父方法的注意事项


一:call() 的作用与使用

     在开始讲解组合继承前我们先来了解一下 call() 方法,call() 方法可以改变 this 的指向,也可以调用函数等等,最主要的还是其改变指向的作用


语法格式 call( 目标this指向,参数1,参数2 ......)

1.1 使用 call() 来调用函数

call() 可以拿来直接用来调用函数


    <script>

       function eat(){

           console.log('我在吃午饭');

       }

       eat.call()

    </script>



1.2 使用 call() 来改变 this 的指向

call() 的第一个参数为你要改变的 this 的指向,这里的 this 指的是 call 的调用者,此处函数调用不指定的话即指向 window,指定让其指向新创建的对象 obj,只需要让其第一个参数为 obj 对象即可,所以结果应该是第一个为 window,第二个为 obj 对象


    <script>

       function eat(){

           console.log(this);

       }

       var obj={

           'name':'小明',

           'age':18

       }  

       eat.call()

       eat.call(obj)

    </script>


二:利用构造函数继承父属性

我们已经知道组合继承是由构造函数和原型对象一起来实现的,其中构造函数实现的是属性的继承,原型对象实现的是方法的继承,这版块就走进利用父构造函数完成属性的继承




2.1 实现过程

    其实现非常容易,只需要在子构造函数中,使用 call 调用父构造函数(将其当做普通函数调用),其中在 call 方法中更改父构造函数中的 this 指向,由于 call 方法是在子构造函数中调用的,所以此处当做参数的 this 代表父构造函数中的 this 指向子构造函数的实例化对象,并传参进去,所以相当于给子构造函数的实例化对象添加了属性并赋值


    <script>

       //声明父构造函数

       function Father(uname,uage,utel,sex){

           this.uname=uname;

           this.uage=uage;

           this.utel=utel;

           this.sex=sex;

       }

       //声明子构造函数,但是想继承父类的uname,uage,utel等等属性的赋值操作

       function Son(uname,uage,utel,sex){

           Father.call(this,uname,uage,utel,sex)

       }

       var son1=new Son('张三',19,12345,'男')

       console.log(son1);

    </script>




2.1 实现过程分析

首先在子构造函数中使用 call 调用了父构造函数,并传参给 call 的参数,其中第一个参数为 this 指向的改变,其余为带入的属性值参数

我们知道构造函数中的 this 指向其实例化对象,所以本身父构造函数的 this 应该指向父构造函数的实例化对象,而此处 call 方法调用在子构造函数中,所以参数的指向更改为指向子构造函数的实例化对象

此处子构造函数的实例化对象就是 son1,所以父构造函数中的 this 指向的均是 son1,

所以就给 son1 添加并赋值了 uname,uage 等等属性



三:利用原型对象继承父方法

组合继承的最后一版块,利用原型对象来继承方法,此处我们说明的是存放在构造函数的原型对象里的公共方法的继承


3.1 继承父方法的错误演示

错误的继承就是直接将父亲的原型对象赋值给子的原型对象,这样确实也可行,但是如果给子原型对象添加子类特有的方法,那父原型对象也会加上这个方法


    <script>

       //声明父构造函数

       function Father(uname,uage){

           this.uname=uname;

           this.uage=uage;

       }

       Father.prototype.money=function(){

           console.log('我有很多钱');

       }

       //声明子构造函数

       Son.prototype=Father.prototype;

       function Son(uname,uage){

           Father.call(this,uname,uage)

       }

       var father1=new Father('爸爸',40)

       var son1=new Son('儿子',19)

       console.log(father1);

       console.log(son1);

    </script>


我们可以发现父子的原型对象中确实都有了这个方法,证明确实这个办法是行得通的




但是其也有问题存在,当我们想给子原型对象单独添加其特有的方法时,就会出问题


上述问题给子原型对象添加特有方法的错误示例:

    <script>

       //声明父构造函数

       function Father(uname,uage){

           this.uname=uname;

           this.uage=uage;

       }

       Father.prototype.money=function(){

           console.log('我有很多钱');

       }

       //声明子构造函数

       Son.prototype=Father.prototype;

       Son.prototype.school=function(){

           console.log('我去上学了');

       }

       function Son(uname,uage){

           Father.call(this,uname,uage)

       }

       var father1=new Father('爸爸',40)

       var son1=new Son('儿子',19)

       console.log(father1);

       console.log(son1);

    </script>


我们发现,我们确实给儿子添加上了儿子特有的方法,但是,父亲的原型对象内也加上了这个方法,这并不满足我们的预期,原因分析如下




问题原因

问题就在于我们的原型对象也是对象,对象是引用数据类型,引用数据类型的对象本质是在堆内存存放,是不能直接访问的,其访问是通过栈内存上的引用地址来找到去访问,而我们此处采用的等号赋值的方式,实际上是将其在栈内存上的引用地址拷贝过去了,二者指向了同一块内存空间,所以更改子原型对象,父原型对象也改变了




3.2 继承父方法的正确做法

正确的做法是让其子原型对象对象等于父实例化对象  Son.prototype=new Father(),其实我感觉有种高内聚低耦合的韵味,减少了直接联系从而解决问题




    <script>

       //声明父构造函数

       function Father(uname,uage){

           this.uname=uname;

           this.uage=uage;

       }

       Father.prototype.money=function(){

           console.log('我有很多钱');

       }

       //声明子构造函数

       Son.prototype=new Father();

       Son.prototype.school=function(){

           console.log('我去上学了');

       }

       function Son(uname,uage){

           Father.call(this,uname,uage)

       }

       var father1=new Father('爸爸',40)

       var son1=new Son('儿子',19)

       console.log(father1);

       console.log(son1);

    </script>


问题得以解决,子原型对象有了自己特有的方法,并且也继承了父亲原型对象中的方法




 3.2 继承父方法的注意事项

我们以 Son.prototype=new Father() 这种方法继承,看似已经天衣无缝,其实我们早就说过,采用等号赋值的方法会造成原型对象被覆盖,里面的构造函数 constructor 会被覆盖掉,需要我们手动返回,所以七千万要记得手动返回 constructor


    <script>

       //声明父构造函数

       function Father(uname,uage){

           this.uname=uname;

           this.uage=uage;

       }

       Father.prototype.money=function(){

           console.log('我有很多钱');

       }

       //声明子构造函数

       Son.prototype=new Father();

       Son.prototype.constructor=Son;  //手动返回构造函数constructor

       Son.prototype.school=function(){

           console.log('我去上学了');

       }

       function Son(uname,uage){

           Father.call(this,uname,uage)

       }

       var father1=new Father('爸爸',40)

       var son1=new Son('儿子',19)

       console.log(father1);

       console.log(son1);

       console.log(Son.prototype.constructor);

    </script>


相关文章
|
29天前
|
JavaScript 前端开发
如何在 JavaScript 中使用 __proto__ 实现对象的继承?
使用`__proto__`实现对象继承时需要注意原型链的完整性和属性方法的正确继承,避免出现意外的行为和错误。同时,在现代JavaScript中,也可以使用`class`和`extends`关键字来实现更简洁和直观的继承语法,但理解基于`__proto__`的继承方式对于深入理解JavaScript的面向对象编程和原型链机制仍然具有重要意义。
|
1月前
|
JavaScript 前端开发
Javascript如何实现继承?
【10月更文挑战第24天】JavaScript 中实现继承的方式有很多种,每种方式都有其优缺点和适用场景。在实际开发中,我们需要根据具体的需求和情况选择合适的继承方式,以实现代码的复用和扩展。
|
1月前
|
JavaScript 前端开发
如何使用原型链继承实现 JavaScript 继承?
【10月更文挑战第22天】使用原型链继承可以实现JavaScript中的继承关系,但需要注意其共享性、查找效率以及参数传递等问题,根据具体的应用场景合理地选择和使用继承方式,以满足代码的复用性和可维护性要求。
|
1月前
|
JavaScript 前端开发 开发者
js实现继承怎么实现
【10月更文挑战第26天】每种方式都有其优缺点和适用场景,开发者可以根据具体的需求和项目情况选择合适的继承方式来实现代码的复用和扩展。
31 1
|
6月前
|
设计模式 JavaScript 前端开发
在JavaScript中,继承是一个重要的概念,它允许我们基于现有的类(或构造函数)创建新的类
【6月更文挑战第15天】JavaScript继承促进代码复用与扩展,创建类层次结构,但过深的继承链导致复杂性增加,紧密耦合增加维护成本,单继承限制灵活性,方法覆盖可能隐藏父类功能,且可能影响性能。设计时需谨慎权衡并考虑使用组合等替代方案。
47 7
|
6月前
|
JavaScript 前端开发
在 JavaScript 中,实现继承的方法有多种
【6月更文挑战第15天】JavaScript 继承常见方法包括:1) 原型链继承,利用原型查找,实例共享原型属性;2) 借用构造函数,避免共享,但方法不在原型上复用;3) 组合继承,结合两者优点,常用但有额外开销;4) ES6 的 class,语法糖,仍基于原型链,提供直观的面向对象编程。
38 7
|
3月前
|
自然语言处理 JavaScript 前端开发
一文梳理JavaScript中常见的七大继承方案
该文章系统地概述了JavaScript中七种常见的继承模式,包括原型链继承、构造函数继承、组合继承、原型式继承、寄生式继承、寄生组合继承等,并探讨了每种模式的实现方式及其优缺点。
一文梳理JavaScript中常见的七大继承方案
|
3月前
|
JavaScript 前端开发
js之class继承|27
js之class继承|27
|
3月前
|
JSON JavaScript 前端开发
js原型继承|26
js原型继承|26
|
3月前
|
JavaScript 前端开发 开发者
JavaScript 类继承
JavaScript 类继承
22 1