JavaScript中常见的七种继承及实现

当需要创建新的对象时,继承是一个必须考虑的问题。JavaScript中的继承方式花样繁多,以下是常见的7种继承方式。

1.原型链继承

将父类的实例作为子类的原型,实现继承,示例代码如下:

function Parent() {
    this.name = 'parent';
}
Parent.prototype.getName = function() {
    return this.name;
};
function Child() {
    this.age=20;
}
Child.prototype=new Parent();
Child.prototype.getAge = function() {
    return this.age;
};

var child = new Child();
console.log(child.getName()); // parent
console.log(child.getAge()); // 20

缺点:原型链继承存在共享父类属性的缺点,对父类属性的修改会影响到所有子类实例。同时,无法向父类构造函数传参。

2.借用构造函数继承

在子类的构造函数中调用父类的构造函数,通过call和apply方法,在子类实例对象上执行构造函数中的代码,从而实现继承。示例如下:

function Parent(name) {
    this.name = name;
    this.getName = function() {
        return this.name;
    };
}
function Child(name, age) {
    // 在子类构造函数中使用apply调用父类构造函数,将父类的name变量绑定到实例对象上
    Parent.apply(this, [name]);
    this.age = age;
    this.getAge = function() {
        return this.age;
    };
}

var child = new Child('lucifer', 20);
console.log(child.getName()); // lucifer
console.log(child.getAge()); // 20

优点:借用构造函数继承解决了共享父类属性的问题,同时可以向父类构造函数传递参数。

缺点:每个子类对象都只能访问它自己的属性和方法,不能共享父类的原型中定义的属性和方法。

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;
Child.prototype.getAge = function() {
    return this.age;
};

var child1 = new Child('lucifer', 20);
child1.colors.push('white');
console.log(child1.getName()); // lucifer
console.log(child1.getAge()); // 20
console.log(child1.colors); // [ 'red', 'green', 'blue', 'white' ]

var child2 = new Child('zhenyuan', 18);
console.log(child2.getName()); // zhenyuan
console.log(child2.getAge()); // 18
console.log(child2.colors); // [ 'red', 'green', 'blue' ]

优点:既继承了父类原型上的方法,又继承了父类构造函数上的属性,属性不会再对象之间共享,可以传参。

缺点:其缺点是调用了两次父类构造函数,生成了两份实例。

4.原型式继承

通过指定一个对象作为新对象的原型,基于已有的对象创建一个新的对象。比实现继承的第一种方式(原型链继承)更加灵活。示例代码如下:

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

var person={
    name:'lucifer',
    colors:['red','green','blue']
}
var child=createObj(person);
child.colors.push('white');

console.log(child.name) // lucifer
console.log(child.colors) // [ 'red', 'green', 'blue', 'white' ]

缺点:浅拷贝,存在引用类型共享的问题。

5.寄生式继承

与原型式继承类似,但略微不同。基本思路是创建一个仅用于封装继承过程的临时构造函数,最后返回这个构造函数。示例代码如下:

function inheritObj(obj) {
    var clone = Object.create(obj);
    clone.sayName = function() {
        console.log('hi');
    };
    return clone;
}
var person = {
    name: 'lucifer',
    colors: ['red', 'green', 'blue']
};
var child = inheritObj(person);

优点:在原型式继承的基础上,添加了新的属性和方法,相对原型式继承更加灵活。

缺点:无法复用父类的方法,与组合继承类似存在两份构造函数和原型。

6.寄生组合式继承

通过借用构造函数来继承属性,通过原型链的混合形式来继承方法。这种继承方式比较经典,是继承的最优解。示例代码如下:

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 = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Child.prototype.getAge = function() {
    return this.age;
};

var child1 = new Child('lucifer', 20);
child1.colors.push('white');
console.log(child1.getName()); // lucifer
console.log(child1.getAge()); // 20
console.log(child1.colors); // [ 'red', 'green', 'blue', 'white' ]

var child2 = new Child('zhenyuan', 18);
console.log(child2.getName()); // zhenyuan
console.log(child2.getAge()); // 18
console.log(child2.colors); // [ 'red', 'green', 'blue' ]

优点:不存在调用父类构造函数两次的问题,完美避免了父类构造函数与子类构造函数之间的耦合,也不存在共享父类属性的问题,同时父类原型上的方法可以正常继承。

7.class继承

ES6新增了class关键字,标准化了JavaScript中的继承方式,可以更简洁地实现继承,代码更易读、易写,利于代码的维护。示例代码如下:

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

class Child extends Parent {
    constructor(name, age) {
        super(name);
        this.age = age;
    }
    getAge() {
        return this.age;
    }
}

const child = new Child('lucifer', 20);
console.log(child.getName()); // lucifer
console.log(child.getAge()); // 20

优点:用class实现继承,简化了代码实现的难度,并且代码更加清晰易理解。

综上,以上七种继承方式各有优劣,具体应根据需求场景选择合适的继承方式。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:JavaScript中常见的七种继承及实现 - Python技术站

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

相关文章

  • visualstudio2017各版本离线安装包获取以及安装教程

    以下是详细讲解“Visual Studio 2017各版本离线安装包获取以及安装教程的完整攻略”的标准Markdown格式文本: Visual Studio 2017各版本离线安装包获取以及安装教程的完整攻略 Visual Studio 2017是微软推出的一款集成开发环境,提供了丰富的工具和功能,用于开发各种类型的应用程序。本文将介绍Visual Stud…

    other 2023年5月9日
    00
  • 怎么删除IE右键的迅雷下载具体修复方法

    让我来为大家详细讲解如何删除IE右键的迅雷下载具体修复方法。 1. 了解问题原因 点击IE的右键弹出菜单,会发现其中出现了“用迅雷下载”等选项,这是由于迅雷软件安装后在注册表中添加了相关设置。如果我们需要删除这些选项,则需要通过修改注册表来实现。 2. 备份注册表 在操作之前,我们首先需要备份注册表。备份方法如下: 按下“Win+R”键,打开运行对话框。 输…

    other 2023年6月27日
    00
  • c#语言assert

    C#语言中的Assert 在C#语言中,Assert是一种用于调试的工具,用于检查程序中的条件是否为真。如果条件为假,Assert会抛一个异常,以便程序员可以及时发现和修复问题。本攻略将详介绍C#语言中的Assert,包括基本概使用方法和示例说明。 基本概念 Assert是C#语言中的一种调试工具,用于检查程序中的条件是否为真。如果条件为假,Assert会抛…

    other 2023年5月6日
    00
  • 详解Linux系统中的tempfs与/dev/shm

    详解Linux系统中的tmpfs与/dev/shm 简介 在Linux系统中,我们经常需要在内存中创建一个文件系统或临时存储区。这时我们就可以使用tmpfs与/dev/shm。tmpfs是一种在内存中创建临时文件系统的机制,而/dev/shm是一个tmpfs挂载点,用于在内存中创建极快的共享内存。在本文中,我们将讨论如何使用tmpfs与/dev/shm。 t…

    other 2023年6月27日
    00
  • bat复制一个文件夹到另一个目录下

    使用bat批处理实现复制文件夹到另一个目录的操作 在Windows环境下,我们经常需要将一个文件夹复制到另一个目录下。使用Windows资源管理器可以完成这个操作,但当需要复制大量的文件夹时,这种方法显然不够高效。这时候我们可以通过批处理的方式来实现一键复制文件夹的操作,提高复制效率。本文将介绍如何使用bat批处理实现复制文件夹到另一个目录下的操作。 1. …

    其他 2023年3月28日
    00
  • grafana安装及使用教程详解

    首先,需要说明的是grafana是一个流行的开源数据可视化工具,可以帮助用户将不同数据源的数据可视化展示。下面是grafana安装及使用教程详解。 安装grafana 下载grafana: 官网下载链接(https://grafana.com/grafana/download) 解压下载的zip文件: unzip grafana-x.x.x.zip 进入gr…

    other 2023年6月27日
    00
  • VBS数组深入浅出

    VBS数组深入浅出 什么是VBS数组? VBS数组是指一组连续的内存空间,用于存储相同类型的数据。VBS数组中的每个元素都有一个唯一的下标,可以通过下标进行访问,修改和删除。 如何创建一个VBS数组? 可以使用Dim语句来声明一个VBS数组,并指定其大小。例如,下面的代码将创建一个名为array1的VBS数组,其中包含5个元素: Dim array1(4) …

    other 2023年6月25日
    00
  • spring(六)之自动装配

    Spring(六)之自动装配 在Spring的IOC容器中,我们可以使用自动装配(Autowiring)来消除手动配置的繁琐,提高开发效率。 自动装配的方式 Spring提供了以下几种自动装配的方式: byName:按属性名自动注入 byType:按属性类型自动注入 constructor:按构造函数参数类型自动注入 autodetect:混合使用byTyp…

    其他 2023年3月28日
    00
合作推广
合作推广
分享本页
返回顶部