JavaScript进阶教程之非extends的组合继承详解

目录
  • 前言
  • 一:call() 的作用与使用
    • 1.1 使用 call() 来调用函数
    • 1.2 使用 call() 来改变 this 的指向
  • 二:利用构造函数继承父属性
    • 2.1 实现过程
    • 2.1 实现过程分析
  • 三:利用原型对象继承父方法
    • 3.1 继承父方法的错误演示
    • 3.2 继承父方法的正确做法
    • 3.2 继承父方法的注意事项
  • 总结

前言

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

一: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>

补充:缺点

就是 “对象原型继承的this对象+prototype对象,都在对象的原型上 suber1._ proto _”,suber1._ proto _上的引用属性任然是共享;

所以就有了我们看到的一句劝告:尽量把属性定义在构造函数内,为了方便继承吧;

还有就是实例一个对象,执行了两次Super()

总结

到此这篇关于JavaScript进阶教程之非extends的组合继承的文章就介绍到这了,更多相关js非extends的组合继承内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2022-08-06

js学习笔记之class类、super和extends关键词

目录 前言 1.es6之前创建对象 2.es6之后class的声明 3.类的继承 4.继承类的静态成员 写在最后 前言 JavaScript 语言在ES6中引入了 class 这一个关键字,在学习面试的中,经常会遇到面试官问到谈一下你对 ES6 中class的认识,同时我们的代码中如何去使用这个关键字,使用这个关键字需要注意什么,这篇来总结一下相关知识点. 正文 1.es6之前创建对象 先来看下es6之前我们要想创建一个对象,只能通过构造函数的方式来创建,将静态方法添加在原型上面使得每一个实例能

javascript中最常用的继承模式 组合继承

复制代码 代码如下: <script type="text/javascript"> //创建基类 function Person(name, age) { this.name = name; this.age = age; } //通过原型方式给基类添加函数(这样可以服用此函数) Person.prototype.showName = function () { alert(this.name); } //创建子类 function Student(name, age,

JS 面向对象之继承---多种组合继承详解

这一次要讲 组合.原型式.寄生式.寄生组合式继承方式. 1. 组合继承:又叫伪经典继承,是指将原型链和借用构造函数技术组合在一块的一种继承方式. 下面来看一个例子: function SuperType(name) { this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function() { alert(this.n

实例介绍JavaScript中多种组合继承

1. 组合继承:又叫伪经典继承,是指将原型链和借用构造函数技术组合在一块的一种继承方式. 下面来看一个例子: function SuperType(name) { this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function() { alert(this.name); } function SubType(nam

ES6 javascript中Class类继承用法实例详解

本文实例讲述了ES6 javascript中Class类继承用法.分享给大家供大家参考,具体如下: 1. 基本用法 Class 之间可以通过extends关键字实现继承, 这比 ES5 的通过修改原型链实现继承, 要清晰和方便很多. class ColorPoint extends Point {} 上面代码定义了一个ColorPoint类, 该类通过extends关键字, 继承了Point类的所有属性和方法. 但是由于没有部署任何代码, 所以这两个类完全一样, 等于复制了一个Point类. 下

JavaScript中的对象继承关系

我们今天就来看一下继承中的类继承以及类继承和原型继承的混用,所谓类继承,就是使用call或者apply方法来进行冒充继承: function Desk(size,height){ this.size=size; this.height=height; } function MJDesk(size,height){ Desk.call(this,size,height);//这种就叫类继承. } var mj = new MJDesk(10,123); 像上面这种就是我们要使用的类继承,用这种继承

实例分析javascript中的异步

js 异步解析 一 .js单线程分析 我们都知道js的一大特点是单线程,也就是同一时间点,只能处理一件事,一句js代码.那为什么js要设计成单线程而不是多线程呢?这主要和js的用途有关,js作为浏览器端的脚本语言,主要的用途为用户与服务端的交互与操作dom.而操作dom就注定了js只能是单线程语言.假如js才取多线程将会出现,多个线程同时对一个dom进行操作的情况,浏览器将无法判断如何渲染.不仅js是单线程,浏览器渲染dom也是单线程的,js的执行和浏览器渲染dom共用的一个线程,这就导致了在h

JavaScript中的类继承

JavaScript Inheritance DouglasCrockfordwww.crockford.com And you think you're so clever and classless and free--John Lennon JavaScript一种没有类的,面向对象的语言,它使用原型继承来代替类继承.这个可能对受过传统的面向对象语言(如C++和Java)训练的程序员来说有点迷惑.JavaScript的原型继承比类继承有更强大的表现力,现在就让我们来看看. Java Jav

JavaScript中创建对象和继承示例解读

对象创建: 当一个函数对象被创建时候,Function构造器产生的函数对象会运行类似这样的代码: 复制代码 代码如下: this.prototype={constructor:this}; 假设函数F F用new方式构造对象时,对象的constructor被设置成这个F.prototype.constructor 如果函数在创建对象前修改了函数的prototype,会影响创建出来对象的construtor属性 如: 复制代码 代码如下: function F(){}; F.prototype={

实例讲解JavaScript中instanceof运算符的用法

instanceof 运算符简介 在 JavaScript 中,判断一个变量的类型尝尝会用 typeof 运算符,在使用 typeof 运算符时采用引用类型存储值会出现一个问题,无论引用的是什么类型的对象,它都返回 "object".ECMAScript 引入了另一个 Java 运算符 instanceof 来解决这个问题.instanceof 运算符与 typeof 运算符相似,用于识别正在处理的对象的类型.与 typeof 方法不同的是,instanceof 方法要求开发者明确地确

JavaScript中的原型继承基础学习教程

大多数编程语言中,都有类和对象,一个类可以继承其他类. 在JavaScript中,继承是基于原型的(prototype-based),这意味着JavaScript中没有类,取而代之的是一个对象继承另一个对象.:) 1. 继承, the proto 在JavaScript中,当一个对象rabbit继承另一了对象animal时,这意味着rabbit对象中将会有一个特殊的属性:rabbit.__proto__ = animal; 当访问rabbit对象时,如果解释器在rabbit中不能找到属性,那么它

实例讲解JavaScript中call、apply、bind方法的异同

以实例切入,讲解JavaScript中call,apply,bind方法,供大家参考,具体内容如下 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <script type="text/javascript"> function MAN(name, sex, age) { this.name =