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

yizhihongxing

当需要创建新的对象时,继承是一个必须考虑的问题。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日

相关文章

  • android设置alpha值来制作透明与渐变效果的实例代码

    Android 中设置 alpha 值可以实现透明及渐变效果。下面分别介绍两种示例: 示例1:实现透明效果 可以通过修改 alpha 值来实现透明效果。alpha 的范围从 0(完全透明)到 1(完全不透明)。 1. 创建布局文件 创建一个 LinearLayout 布局文件,然后将它的 android:background 属性设置为一个颜色值,以便更容易…

    其他 2023年4月16日
    00
  • vue如何使用rem适配

    使用rem适配是多种移动端页面适配方案中比较常用的一种。下面我将详细讲解在Vue中如何使用rem适配的完整攻略。 步骤一:配置viewport 在html文件头部添加如下代码: <meta name="viewport" content="width=device-width, initial-scale=1, maxim…

    other 2023年6月27日
    00
  • php面试中关于面向对象的相关问题

    PHP面试中关于面向对象的相关问题攻略 面向对象编程(Object-Oriented Programming,简称OOP)是PHP开发中的重要概念。在PHP面试中,面向对象的相关问题经常被提及。下面是一些常见的面向对象问题以及它们的详细解释和示例。 1. 什么是面向对象编程? 面向对象编程是一种编程范式,它将数据和操作数据的方法封装在一起,形成对象。对象是类…

    other 2023年8月20日
    00
  • java面向对象继承与多态介绍

    Java面向对象继承与多态介绍 继承的定义及作用 继承是指一个类继承(获取)另一个类的属性和方法,被继承的类称为父类(也称为基类、超类),继承的类称为子类(派生类)。继承可以使代码复用和扩展程序。子类可以使用父类的方法和属性,同时还可以根据需求重写父类的方法或者添加新的方法和属性。 示例代码: public class Animal { private St…

    other 2023年6月26日
    00
  • jpa 使用@Column来定义字段类型

    当使用JPA定义实体类时,有时需使用@Column注解来定义字段类型。下面是使用@Column注解来定义字段类型的完整攻略: 一、定义字段类型 在定义实体类时,需要使用@Column注解来定义字段类型。具体实现如下: @Entity @Table(name="user") public class User { @Id @Generate…

    other 2023年6月25日
    00
  • Java关于含有继承类的成员初始化过程讲解

    Java关于含有继承类的成员初始化过程讲解 在Java中,含有继承类的成员初始化过程比较复杂。本文将从以下几个方面详细讲解初始化过程:继承、实例化、构造函数和静态变量初始化。通过多个示例的说明,让读者更加深入地理解Java中含有继承类的成员初始化过程。 继承 在Java中,子类继承了父类的属性和方法,但是并不包括构造函数。因此,在实例化子类时,需要先实例化父…

    other 2023年6月20日
    00
  • version-“rtm”版本是什么意思?

    “version-rtm”版本的含义攻略 在本攻略中,我们将介绍“version-rtm”版本的含义和用途。我们将提供两个示例说明,一个是Windows操作系统中的版本号,另一个是Visual Studio集成开发环境中的版本号。 “version-rtm”版本的含义 “version-rtm”是一个软件版本的命名约定,其中“rtm”代表“Release t…

    other 2023年5月8日
    00
  • C++的类型转换(强转)你了解吗

    当我们需要将一种类型的数据转换为另一种类型时,可以使用C++的类型转换。其中,强制类型转换是一种向编译器发出的指令,强制将一个数据类型转换为另一种数据类型。在C++中,强制类型转换有三种方式,分别是static_cast、reinterpret_cast和const_cast。 static_cast static_cast用于通常的转换操作,例如将整数转为…

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