js继承的这6种方式!(上)

js继承的这6种方式!(上)

什么是继承?

在面向对象编程中,继承是指一个新类从一个现有的类继承了一些方法和属性。被继承的类称为父类(或基类、超类),新类称为子类(或派生类)。

继承的好处

  • 可以使用父类已经定义好的属性和方法,减少重复的代码;
  • 提高代码的可扩展性和可维护性。

继承的6种方式

下面,让我们逐个详细介绍js中的6种继承方式。

1. 原型链继承

原型链继承是常见的继承方式,它的本质是重写原型对象。我们可以在父类的原型上定义实例方法或属性,这样子类也能够访问和使用这些方法或属性。

function Parent() {
    this.name = ['John', 'Mary'];
}
Parent.prototype.getName = function() {
    return this.name;
};

function Child() {}
Child.prototype = new Parent();

var child1 = new Child();
console.log(child1.getName()); // ['John', 'Mary']

注:当我们在子类中调用 getName方法的时候,实际上它是通过在原型链上查找到父类的 getName方法并被调用的。

但是需要注意的是,如果在子类实例中修改了继承的数组 name,那么这个修改会影响到父类和其他子类,这样会产生一些难以预知的问题。因此,原型链继承主要存在如下缺点:

  1. 无法传递参数。如果子类想对父类的构造函数传递一些参数,那么必须修改子类的原型对象或者通过另外的方式来实现;
  2. 共享引用类型的属性。原型链继承会使子类实例共享父类实例创建的引用类型的属性。这导致子类实例间存在一个难以分离的引用。

2. 构造函数继承

构造函数继承也叫做经典继承,它的本质是在子类的构造函数中调用父类的构造函数,并用它的证书属性来扩展子类的实例。

function Parent(name) {
    this.name = name;
    this.getName = function() {
        return this.name;
    };
}

function Child(name) {
    Parent.call(this, name);
}

var child1 = new Child('John');
console.log(child1.getName()); // 'John'

注:在 Child函数内部,我们调用了Parent构造函数并改变了它的this值。这样就能通过子类的实例 child1 来访问来自父类的属性或方法了。

构造函数继承虽然弥补了原型链继承的短板,例如子类实例共享父类实例的问题,但是它也存在一些缺点:

  1. 父类的方法没有被复用,每个子类都有一个独立的方法实例;
  2. 只能继承父类的实例属性,不能继承父类的原型属性和方法。这使得构造函数继承无法实现函数复用,导致子类实例之间不能共享方法和属性。

3. 组合继承

组合继承是常用的继承方式,它结合了原型链继承和构造函数继承的优点。

function Parent(name) {
    this.name = name;
    this.colors = ['red', 'green', 'blue'];
}
Parent.prototype.getName = function() {
    return this.name;
};

function Child(name, age) {
    Parent.call(this, name);
    this.age = age;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;

var child1 = new Child('John', 18);
child1.colors.push('black');
console.log(child1.getName(), child1.age, child1.colors); // John 18 ['red', 'green', 'blue', 'black']

var child2 = new Child('Mary', 20);
console.log(child2.getName(), child2.age, child2.colors); // Mary 20 ['red', 'green', 'blue']

注:在 Child函数内部,我们调用了Parent构造函数并改变了它的this值。这样就能复用来自父类的方法实例了。同时又将父类的实例属性导入子类实例中,并通过继承父类的原型对象来使得子类也能使用来自父类的实例方法和原型方法。

组合继承是javascript中最常用的继承方式之一,但也存在缺点:每次在创建子类实例的时候,都会调用一次父类的构造函数导致原型链中存在多余的执行。

4. 原型式继承

原型式继承的核心是利用一个只用于临时对象创建的构造函数作为桥梁。本质上,就是对原型对象的继承。

function object(o) {
    function F(){}
    F.prototype = o;
    return new F();
}

var person = {
    name: 'John',
    colors: ['red', 'green', 'blue']
};

var child1 = object(person);
var child2 = object(person);
child1.colors.push('black');
console.log(child1.name, child1.colors); // 'John', ['red', 'green', 'blue', 'black']
console.log(child2.colors); // ['red', 'green', 'blue', 'black']

原型式继承通过一个工厂模式返回一个新对象,并在新对象与原型对象之间创建了一个关联。通过这种关联,子类可以访问原型对象的所有属性和方法。但是,与原型链继承相似的是,原型式继承也存在着与原型链继承相同的缺点。

5. 寄生式继承

寄生式继承是原型式继承的一种增强版,其本质是对原型式继承(或其他继承方式)的一种优化策略。

function object(o) {
    function F(){}
    F.prototype = o;
    return new F();
}

function createAnother(original) {
    var clone = object(original);
    clone.getColors = function() {
        return this.colors;
    };
    return clone;
}

var person = {
    name: 'John',
    colors: ['red', 'green', 'blue']
};

var child1 = createAnother(person);
var child2 = createAnother(person);
child1.colors.push('black');
console.log(child1.getColors()); // ['red', 'green', 'blue', 'black']
console.log(child2.getColors()); // ['red', 'green', 'blue']

注:在使用寄生式继承的过程中,我们可以在返回的对象上面添加额外的属性或方法,使其能够满足子类自身的需求。

寄生式继承相较于原型式继承,其可以引用传入的参数,相对于构造函数继承和组合继承,它的性能要更加的优秀。

6. 寄生组合式继承

虽然组合式继承结合了原型链继承和构造函数继承的优点,但也存在着问题,即在子类中会重复调用构造函数。

针对这个问题,我们可以使用寄生组合式继承来实现这个操作。寄生组合式继承先是利用父类的拷贝实现复制父类的实例属性和方法,然后在通过将子类原型指向父类的拷贝来继承父类原型的方法。

function inhertPrototype(subType, superType) {
    var prototype = Object.create(superType.prototype);
    prototype.constructor = subType;
    subType.prototype = prototype;
}

function Parent(name) {
    this.name = name;
    this.colors = ['red', 'green', 'blue'];
}
Parent.prototype.getName = function() {
    return this.name;
};

function Child(name, age) {
    Parent.call(this, name);
    this.age = age;
}
// 在此处完成了引用类型属性的复制
inheritPrototype(Child, Parent);

var child1 = new Child('John', 18);
child1.colors.push('black');
console.log(child1.getName(), child1.age, child1.colors); // John 18 ['red', 'green', 'blue', 'black']

var child2 = new Child('Mary', 20);
console.log(child2.getName(), child2.age, child2.colors); // Mary 20 ['red', 'green', 'blue']

寄生组合式继承是继承中的经典法属。相较于组合式继承,寄生组合式继承最大的优点就是避免了在使用组合式继承时对父类的呼叫。

以上就是js中的六种继承方式,可以根据不同的需求在实际开发中进行选择。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:js继承的这6种方式!(上) - Python技术站

(0)
上一篇 2023年6月26日
下一篇 2023年6月26日

相关文章

  • java-具有阻塞的heaptaskdaemon线程的anr

    Java中具有阻塞的HeapTaskDaemon线程的ANR攻略 ANR(Application Not Responding)是Java应用程序中常见的问题之一,它通常是由于主线程被阻塞导致的。在Java中,也存在类似,例如具有阻塞的HeapTaskDaemon线程的ANR。本文将提供一个完整攻略,包括ANR的定义、原因解方法以及示例说明等。 1. ANR…

    other 2023年5月8日
    00
  • 微信小程序列表渲染功能之列表下拉刷新及上拉加载的实现方法分析

    微信小程序列表渲染功能之列表下拉刷新及上拉加载的实现方法分析 一、前言 在微信小程序开发中,列表渲染功能是必不可少的功能之一。而列表下拉刷新及上拉加载是列表渲染的常见需求,本文将从实现方法分析角度对列表下拉刷新及上拉加载这一功能进行详细讲解。 二、实现思路 1. 下拉刷新 下拉刷新的实现思路如下: 在需要下拉刷新的页面添加一个scroll-view元素,并设…

    other 2023年6月25日
    00
  • jvm虚拟机类加载机制详解

    jvm虚拟机类加载机制详解 什么是类加载 在 Java 程序中,类的加载是指将类的 .class 文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个 java.lang.Class 对象,用来封装在方法区内的数据结构。ClassLoader 类是用来加载 Java 类的类加载器。 类加载的步骤 Java 虚拟机将符号引用转换成…

    other 2023年6月25日
    00
  • 鼠标右键刷新电脑反应很慢该怎么办?

    问题描述: 在使用电脑时,我们经常会使用鼠标右键进行各种操作,其中就包括刷新页面,但是有时我们会发现鼠标右键刷新页面的反应很慢,甚至会出现卡顿、卡死的情况。这种情况该怎么办呢? 解决方案: 一、检查系统垃圾文件并清理 系统中的垃圾文件和多余的程序会占用电脑的资源,并使电脑变慢。我们可以使用系统清理工具或其他第三方工具进行清理。以下是使用Windows自带的D…

    other 2023年6月27日
    00
  • Android 自定义星评空间示例代码

    Android 自定义星评空间示例代码攻略 本文将详细讲解自定义星评空间示例代码的实现过程。星评空间可以用于用户评分等场景,采用自定义控件实现,较为灵活。具体步骤如下: 1.设计界面和布局 首先,需要在Android Studio中新建一个布局文件,设计星级评分控件的UI界面。依据需求,可以添加TextView、ImageView等视图。其中,TextVie…

    other 2023年6月25日
    00
  • python递归&迭代方法实现链表反转

    接下来我将详细讲解如何使用Python的递归和迭代方法实现链表的反转。 什么是链表反转 链表反转(reverse a linked list)指的是将链表中的所有节点的指针方向都倒转,即原来指向下一个节点的指针变为指向前一个节点,这样可以让链表的尾部变为头部,实现链表的逆序。 实现方法 链表反转可以使用递归和迭代两种方法进行实现。 递归方法 递归反转链表的思…

    other 2023年6月27日
    00
  • 怎样批量修改文件为不同文件名?批量修改文件为不同文件名方法

    要批量修改文件为不同文件名,您可以使用命令行工具或脚本语言。 使用命令行工具 1.使用cp命令复制多个文件并修改文件名 cp old_file1 new_file1 && cp old_file2 new_file2 && cp old_file3 new_file3 使用&&运算符,可以在一个命令行中同时执行…

    other 2023年6月26日
    00
  • vue px转rem配置详解

    Vue px转rem配置详解 什么是px和rem px:是像素,是网页最常用的长度单位。 rem:是一种相对单位,它是根据根元素的字体大小而定的单位,也就是说当根元素的字体大小发生变化时,原本以rem作为单位的元素也会随之改变。 为什么需要将px转为rem 移动端屏幕尺寸多种多样,我们使用不同的屏幕访问页面就会发现页面布局等效果有差异。 根据设备宽度动态改变…

    other 2023年6月27日
    00
合作推广
合作推广
分享本页
返回顶部