JavaScript 继承的实现

JavaScript 继承的实现攻略

JavaScript 是一种基于原型的语言,这使得继承变得更为简单。通过原型继承,对象可以继承另一个对象的属性和方法。继承的实现方式有多种,包括原型链继承、构造函数继承、组合继承、寄生继承、原型式继承和类继承等。下面将依次介绍这些继承的实现方式。

1. 原型链继承

原型链继承利用 JavaScript 的原型链机制,把子类的原型链指向父类的实例。这种方式实现简单,并且可以实现继承父类的所有属性和方法。但是有一个致命缺陷,就是如果父类的引用类型属性被修改,子类的引用类型属性也会随之改变。示例如下:

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

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

var child1 = new Child();
var child2 = new Child();

child1.colors.push('yellow');

console.log(child1.colors); // ["red", "green", "blue", "yellow"]
console.log(child2.colors); // ["red", "green", "blue", "yellow"]

上面的代码中,父类 Parent 有一个属性 colors,它是一个数组。当我们定义子类 Child 并继承 Parent 时,子类的原型指向父类的实例。如果我们向 child1 的 colors 属性中添加一个新的元素,child2 的 colors 也发生了变化。这是因为 child1 和 child2 的 colors 引用的是同一个数组,从而导致共享一个实例。这样的结果有时会是我们所期望的,但有时却不是。

2. 构造函数继承

构造函数继承通过在子类型的构造函数中调用父类型的构造函数来实现继承。这种方式可以解决在原型链继承中父类引用类型属性共享的问题。但是这种方式无法继承父类原型上的属性和方法。示例如下:

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

function Child() {
    Parent.call(this);
    this.age = 18;
}

var child = new Child();

console.log(child.name); // "Parent"
console.log(child.colors); // ["red", "green", "blue"]
console.log(child.age); // 18
console.log(child.sayName); // undefined

在上面的示例中,子类 Child 的构造函数中调用了父类 Parent 的构造函数,并且使用 call 方法改变了 this 的指向。这样就只继承了父类的属性和方法,而没有继承原型上的属性和方法。

3. 组合继承

组合继承是指将原型链继承和构造函数继承组合起来使用。这种方式可以继承父类原型上的属性和方法,并且可以避免引用类型属性共享的问题。但是这种方式会调用两次父类的构造函数,一次是在创建子类原型的时候调用,另一次是在子类的构造函数中调用。示例如下:

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

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

var child1 = new Child();
var child2 = new Child();

child1.colors.push('yellow');

console.log(child1.name); // "Parent"
console.log(child1.colors); // ["red", "green", "blue", "yellow"]
console.log(child1.age); // 18
console.log(child1.sayName()); // "Parent"
console.log(child2.name); // "Parent"
console.log(child2.colors); // ["red", "green", "blue"]
console.log(child2.age); // 18
console.log(child2.sayName()); // "Parent"

在上面的代码中,首先通过在子类的构造函数中调用父类的构造函数来继承父类的属性。然后将子类的原型设置为一个父类的实例,从而继承父类原型上的属性和方法。需要注意的是,由于将子类的原型设置为父类的实例,所以在创建子类的实例时会调用两次父类的构造函数。

4. 寄生继承

寄生继承是指在原型式继承的基础上,增强对象,然后返回对象。这种方式通过创建一个用于封装继承过程的函数,其中该函数以某种方式来增强对象,最后再返回对象来实现继承。示例如下:

function Parent() {
    this.name = 'Parent';
}
Parent.prototype.sayName = function () {
    console.log(this.name);
}

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

function create(parent) {
    var child = object(parent.prototype);
    child.constructor = Child;
    return child;
}

function Child() {
    Parent.call(this);
    this.age = 18;
}

Child.prototype = create(Parent);

var child = new Child();

console.log(child.name); // "Parent"
console.log(child.age); // 18
console.log(child.sayName()); // "Parent"

在上面的代码中,我们使用了一个 helper 函数 object() 来创建一个新的对象,然后将其原型设置为一个父类的实例。最后,我们创建了一个 create() 函数来实现父类的实例化,并将其赋给子类的原型。这样我们就可以在子类的构造函数中调用父类的构造函数来继承属性,同时通过增强对象继承父类原型上的属性和方法。

5. 原型式继承

原型式继承是在不使用构造函数的情况下,实现对象之间的继承。这种方式背后的思想是借助已有的对象来创建新的对象,并且重写其属性和方法。虽然这种方式看起来非常简单,但是由于使用的是引用类型,所以容易出现引用类型属性共享的问题。示例如下:

var parent = {
    name: 'parent',
    colors: ['red', 'green', 'blue'],
    sayName: function () {
        console.log(this.name);
    }
};

var child1 = Object.create(parent);
var child2 = Object.create(parent);

child1.name = 'child1';
child1.colors.push('yellow');

console.log(child1.name); // "child1"
console.log(child1.colors); // ["red", "green", "blue", "yellow"]
console.log(child1.sayName()); // "child1"
console.log(child2.name); // "parent"
console.log(child2.colors); // ["red", "green", "blue", "yellow"]
console.log(child2.sayName()); // "parent"

在上面的代码中,我们通过 Object.create() 方法来创建了一个新的对象 child,同时将其原型指向了一个父类对象 parent。接着,我们修改了 child1 的 name 属性,并向其 colors 属性中添加了一个新的元素,但是修改操作也影响了 child1 的父对象 parent。因为 parent 对象是一个引用类型,被 child1 和 child2 所共享。

6. 类继承

ES6 中引入了 class 关键字来实现面向对象编程。类继承是指通过继承 class 中的属性和方法来实现继承。类继承相比于上述的继承方式更加直观,易于理解。示例如下:

class Parent {
    constructor() {
        this.name = 'parent';
        this.colors = ['red', 'green', 'blue'];
    }
    sayName() {
        console.log(this.name);
    }
}

class Child extends Parent {
    constructor() {
        super();
        this.age = 18;
    }
}

var child = new Child();

console.log(child.name); // "parent"
console.log(child.age); // 18
console.log(child.sayName()); // "parent"
console.log(child instanceof Parent); // true
console.log(child instanceof Child); // true

在上面的代码中,我们定义了一个父类 Parent 和一个子类 Child,并且通过 extends 关键字让子类继承父类的属性和方法。在子类的构造函数中使用 super() 关键字来调用父类的构造函数。这样就实现了子类的继承。

通过上述 6 种继承方式的比较,我们可以看出,不同的应用场景有不同的继承方式选择。对于继承应该如何实现,要根据实际情况做出权衡和选择。

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

(0)
上一篇 2023年5月18日
下一篇 2023年5月18日

相关文章

  • setTimeout函数兼容各主流浏览器运行执行效果实例

    下面我就来详细讲解一下如何使用 setTimeout 函数兼容各主流浏览器运行执行效果的完整攻略。 1. setTimeout 函数的基本使用 setTimeout 函数是 JavaScript 中一个比较常见的函数,用于在一定时间后执行一些操作。其基本语法为: setTimeout(function, milliseconds, param1, param…

    JavaScript 2023年6月11日
    00
  • Javascript核心读书有感之类型、值和变量

    Javascript核心读书有感之类型、值和变量 类型 Javascript有7种数据类型,分为两类:原始类型和引用类型。 原始类型 原始类型有5种,分别是:Number、Boolean、String、Null、Undefined。 Number: 数字类型,包括整数和浮点数。可以进行数学运算。 Boolean: 布尔类型,只有 true 和 false 两…

    JavaScript 2023年5月18日
    00
  • 详解操作cookie的原生方法cookieStore

    下面是详解操作cookie的原生方法cookieStore的完整攻略。 一、什么是cookieStore cookieStore 是浏览器 JavaScript 运行时的一个 API,用来管理浏览器中所有的 cookie,可用于对 cookie 实现增删改查等操作。 二、cookieStore的基本使用方法 cookieStore API 可以使用在浏览器中…

    JavaScript 2023年6月11日
    00
  • 使用JS获取SessionStorage的值

    获取SessionStorage的值是前端开发过程中常用的操作之一,下面是使用JavaScript获取SessionStorage的详细步骤: 1. 确认SessionStorage已经存储了值 在执行获取SessionStorage的值之前,我们需要先确认SessionStorage中已经存储了需要获取的值。存储SessionStorage的方式一般有两种…

    JavaScript 2023年6月11日
    00
  • JavaScript定义函数的三种实现方法

    下面就为大家详细讲解JavaScript定义函数的三种实现方法。 方法一:函数声明 函数声明是定义函数的最基本方法。 语法格式如下: function functionName(arg1, arg2, …) { //函数体 } 其中 functionName 是函数名,arg1, arg2, … 是形参,函数体可以是任意 JavaScript 代码。…

    JavaScript 2023年5月27日
    00
  • Js放到HTML文件中的哪个位置有什么区别

    JavaScript 代码可以放在 HTML 文档的不同位置,包括 <head> 标签中和 <body> 标签中。 把 JavaScript 放在标签中 首先,将JavaScript代码放在 标签中,可以使 JavaScript 代码在页面加载之前被加载。考虑到现代web应用程序的性能和用户体验,有效地加载 JavaScript 对于…

    JavaScript 2023年5月27日
    00
  • JavaScript对象的特性与实践应用深入详解

    一、JavaScript对象的特性 对象的定义:对象是一种复合值,它将很多值(原始值或其他对象)聚合在一起,可以通过标识符(属性名)来访问这些值。对象有两种类型:内置对象和宿主对象。 对象的属性:每个JavaScript对象都是一个属性的容器,它们都有自己的属性集。对象的属性是由一个键值对组成,键是字符串类型,值可以是任意类型的JavaScript值,包括原…

    JavaScript 2023年5月27日
    00
  • JavaScript 微任务和宏任务讲解

    JavaScript 微任务和宏任务讲解 在 JavaScript 中,异步任务主要分为微任务和宏任务两种。宏任务可以理解为宏大的任务,就是需要花费比较长时间才能完成的任务,比如定时器、ajax 请求、DOM 事件等。微任务可以理解为小任务,比较轻量,基本上可以在当前任务完成后马上被执行,比如 Promise。 在执行异步任务时,引擎会将宏任务放入宏任务队列…

    JavaScript 2023年5月28日
    00
合作推广
合作推广
分享本页
返回顶部