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>


相关文章
|
3月前
|
JavaScript 前端开发
如何在 JavaScript 中使用 __proto__ 实现对象的继承?
使用`__proto__`实现对象继承时需要注意原型链的完整性和属性方法的正确继承,避免出现意外的行为和错误。同时,在现代JavaScript中,也可以使用`class`和`extends`关键字来实现更简洁和直观的继承语法,但理解基于`__proto__`的继承方式对于深入理解JavaScript的面向对象编程和原型链机制仍然具有重要意义。
|
3月前
|
JavaScript 前端开发
如何使用原型链继承实现 JavaScript 继承?
【10月更文挑战第22天】使用原型链继承可以实现JavaScript中的继承关系,但需要注意其共享性、查找效率以及参数传递等问题,根据具体的应用场景合理地选择和使用继承方式,以满足代码的复用性和可维护性要求。
|
3月前
|
JavaScript 前端开发 开发者
js实现继承怎么实现
【10月更文挑战第26天】每种方式都有其优缺点和适用场景,开发者可以根据具体的需求和项目情况选择合适的继承方式来实现代码的复用和扩展。
45 1
|
3月前
|
JavaScript 前端开发
Javascript如何实现继承?
【10月更文挑战第24天】JavaScript 中实现继承的方式有很多种,每种方式都有其优缺点和适用场景。在实际开发中,我们需要根据具体的需求和情况选择合适的继承方式,以实现代码的复用和扩展。
|
5月前
|
自然语言处理 JavaScript 前端开发
一文梳理JavaScript中常见的七大继承方案
该文章系统地概述了JavaScript中七种常见的继承模式,包括原型链继承、构造函数继承、组合继承、原型式继承、寄生式继承、寄生组合继承等,并探讨了每种模式的实现方式及其优缺点。
一文梳理JavaScript中常见的七大继承方案
|
5月前
|
JavaScript 前端开发
js之class继承|27
js之class继承|27
|
5月前
|
JSON JavaScript 前端开发
js原型继承|26
js原型继承|26
|
5月前
|
JavaScript 前端开发 开发者
JavaScript 类继承
JavaScript 类继承
31 1
|
5月前
|
JavaScript 前端开发
JS的几种继承方式
JavaScript中的几种继承方式视频。
34 0
|
6月前
|
开发者 图形学 iOS开发
掌握Unity的跨平台部署与发布秘籍,让你的游戏作品在多个平台上大放异彩——从基础设置到高级优化,深入解析一站式游戏开发解决方案的每一个细节,带你领略高效发布流程的魅力所在
【8月更文挑战第31天】跨平台游戏开发是当今游戏产业的热点,尤其在移动设备普及的背景下更为重要。作为领先的游戏开发引擎,Unity以其卓越的跨平台支持能力脱颖而出,能够将游戏轻松部署至iOS、Android、PC、Mac、Web及游戏主机等多个平台。本文通过杂文形式探讨Unity在各平台的部署与发布策略,并提供具体实例,涵盖项目设置、性能优化、打包流程及发布前准备等关键环节,助力开发者充分利用Unity的强大功能,实现多平台游戏开发。
212 0

热门文章

最新文章

  • 1
    当面试官再问我JS闭包时,我能答出来的都在这里了。
    43
  • 2
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    27
  • 3
    Node.js 中实现多任务下载的并发控制策略
    33
  • 4
    【2025优雅草开源计划进行中01】-针对web前端开发初学者使用-优雅草科技官网-纯静态页面html+css+JavaScript可直接下载使用-开源-首页为优雅草吴银满工程师原创-优雅草卓伊凡发布
    26
  • 5
    【JavaScript】深入理解 let、var 和 const
    49
  • 6
    【04】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架二次开发准备工作-以及建立初步后端目录菜单列-优雅草卓伊凡商业项目实战
    47
  • 7
    【03】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架搭建-服务端-后台管理-整体搭建-优雅草卓伊凡商业项目实战
    57
  • 8
    【02】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-ui设计图figmaUI设计准备-figma汉化插件-mysql数据库设计-优雅草卓伊凡商业项目实战
    57
  • 9
    如何通过pm2以cluster模式多进程部署next.js(包括docker下的部署)
    72
  • 10
    【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
    55