浅谈JavaScript面向对象–继承

浅谈JavaScript面向对象 - 继承

什么是继承

在面向对象编程中,继承是指一个对象直接使用另一个对象的属性和方法的能力。被继承的对象称为父类或超类,继承它的对象称为子类或派生类。子类可以继承父类的所有公共方法和属性,同时还可以根据需求添加新的属性或方法。

JavaScript中的继承是基于原型(Prototype)实现的,每个对象都可以拥有原型,并继承从原型中继承来的属性和方法。

继承的实现方法

原型链继承

原型链继承是指让子类的原型指向父类的实例,从而实现继承。

// 定义一个Animal类
function Animal(name) {
  this.name = name
}

// Animal类的实例方法
Animal.prototype.sayName = function() {
  console.log(`My name is ${this.name}`)
}

// 定义一个Dog类,继承Animal类
function Dog(name, age) {
  this.age = age
}

// Dog类的原型指向Animal类的实例
Dog.prototype = new Animal()

// Dog类的实例方法
Dog.prototype.sayAge = function() {
  console.log(`My age is ${this.age}`)
}

// 创建一个Dog的实例,并调用它的方法
const dog = new Dog('旺财', 2)
dog.sayName() // My name is 旺财
dog.sayAge() // My age is 2

这种方式实现起来简单,但存在一个问题:如果父类的实例属性是引用类型,那么子类实例共享这个引用属性。比如:

function Animal() {
  this.colors = ['黑色', '白色', '棕色']
}

function Dog() {}

Dog.prototype = new Animal()

const dog1 = new Dog()
const dog2 = new Dog()

// dog1和dog2的colors属性共享了同一个数组对象
dog1.colors.push('灰色')
console.log(dog2.colors) // ['黑色', '白色', '棕色', '灰色']

借用构造函数继承

通过调用父类的构造函数,实现子类继承父类的构造函数中的属性。这种方式可以解决上面提到的共享引用属性的问题。

// 定义一个Animal类
function Animal(name) {
  this.name = name
  this.colors = ['黑色', '白色', '棕色']
}

// 定义一个Dog类,继承Animal类
function Dog(name, age) {
  Animal.call(this, name) // 调用Animal类的构造函数
  this.age = age
}

// 创建一个Dog的实例,并调用它的方法
const dog = new Dog('大黄', 2)
console.log(dog.name) // 大黄
console.log(dog.age) // 2

dog.colors.push('灰色')
console.log(dog.colors) // ['黑色', '白色', '棕色', '灰色']

const dog2 = new Dog('二狗', 3)
console.log(dog2.colors) // ['黑色', '白色', '棕色']

这种方式的缺点是父类原型中的方法无法被子类继承。

组合式继承

组合式继承结合了原型链继承和借用构造函数继承的优点,在子类的构造函数中调用父类的构造函数,同时将子类的原型指向父类的实例。

// 定义一个Animal类
function Animal(name) {
  this.name = name
  this.colors = ['黑色', '白色', '棕色']
}

// Animal类的实例方法
Animal.prototype.sayName = function() {
  console.log(`My name is ${this.name}`)
}

// 定义一个Dog类,继承Animal类
function Dog(name, age) {
  Animal.call(this, name) // 借用父类的构造函数
  this.age = age
}

// Dog的原型指向Animal类的实例
Dog.prototype = new Animal()

// Dog类的实例方法
Dog.prototype.sayAge = function() {
  console.log(`My age is ${this.age}`)
}

// 创建一个Dog的实例,并调用它的方法
const dog = new Dog('旺财', 2)
dog.sayName() // My name is 旺财
dog.sayAge() // My age is 2

dog.colors.push('灰色')
console.log(dog.colors) // ['黑色', '白色', '棕色', '灰色']

const dog2 = new Dog('大黄', 3)
console.log(dog2.colors) // ['黑色', '白色', '棕色']

这样子类实例既可以继承父类的实例属性和方法,也可以继承父类原型中的属性和方法。

示例说明

示例一

下面我们来看一个更具体的例子,使用面向对象的方式实现一个圆形类和一个长方形类,来演示继承的使用。

// 定义一个Shape基类
class Shape {
  constructor(color) {
    this.color = color
  }

  draw() {
    console.log(`画出一个${this.color}的形状。`)
  }
}

// 定义一个Circle圆形类,继承Shape类
class Circle extends Shape {
  constructor(color, r) {
    super(color)
    this.r = r
  }

  draw() {
    console.log(`画出一个${this.color}的圆形,半径为 ${this.r}。`)
  }
}

// 定义一个Rectangle长方形类,继承Shape类
class Rectangle extends Shape {
  constructor(color, width, height) {
    super(color)
    this.width = width
    this.height = height
  }

  draw() {
    console.log(`画出一个${this.color}的长方形,宽度为 ${this.width},高度为 ${this.height}。`)
  }
}

// 创建一个Circle圆形类和一个Rectangle长方形类的实例
const circle = new Circle('红色', 10)
circle.draw() // 画出一个红色的圆形,半径为 10。

const rectangle = new Rectangle('蓝色', 20, 30)
rectangle.draw() // 画出一个蓝色的长方形,宽度为 20,高度为 30。

示例二

继承也可以通过ES6的class和extends关键字来实现。

// 定义一个Animal类
class Animal {
  constructor(name) {
    this.name = name
  }

  // Animal类的实例方法
  sayName() {
    console.log(`My name is ${this.name}`)
  }
}

// 定义一个Dog类,继承Animal类
class Dog extends Animal {
  constructor(name, age) {
    super(name)
    this.age = age
  }

  // Dog类的实例方法
  sayAge() {
    console.log(`My age is ${this.age}`)
  }
}

// 创建一个Dog的实例,并调用它的方法
const dog = new Dog('旺财', 2)
dog.sayName() // My name is 旺财
dog.sayAge() // My age is 2

总结

以上是JavaScript中几种常见的继承实现方式,不同的继承方式各有优缺点,在实际的开发中我们需要根据具体的情况选择不同的继承方式。同时在ES6中,类和继承的语法已经得到了进一步的简化,我们可以直接使用class和extends关键字来定义类和继承。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:浅谈JavaScript面向对象–继承 - Python技术站

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

相关文章

  • C语言中动态内存分配malloc、calloc和realloc函数解析

    C语言中动态内存分配函数解析 在C语言中,动态内存分配是一种重要的技术,它允许程序在运行时动态地分配和释放内存。C语言提供了几个函数来实现动态内存分配,其中包括malloc、calloc和realloc函数。本文将详细解析这三个函数的用法和区别。 1. malloc函数 malloc函数用于在堆上分配指定大小的内存块。它的函数原型如下: void* mall…

    other 2023年8月2日
    00
  • Android Fragment 基本了解(图文介绍)

    Android Fragment 基本了解(图文介绍) 什么是 Fragment? Fragment 是一种 UI 组件,可以像 Activity 一样具有用户界面,并且可以在 Activity 中组合使用多个 Fragment 以构建复杂的用户界面。 Fragment 的使用场景 Fragment 的使用场景主要涉及以下几种情况: 在大屏幕设备(比如平板电…

    other 2023年6月27日
    00
  • SpringCloud环境搭建过程之Rest使用小结

    下面详细讲解一下SpringCloud环境搭建过程之Rest使用小结。 什么是Spring Cloud Spring Cloud 是一个基于 Spring Boot 的开发平台,为分布式系统中的一些常见模式、协议、服务提供了一种简单的方法进行构建和管理。它提供了一整套的技术框架,包括服务发现注册、配置中心、客户端负载均衡、断路器、网关路由等等。 Spring…

    other 2023年6月27日
    00
  • Mybatis select记录封装的实现

    “Mybatis select记录封装的实现”指的是在Mybatis框架中如何将从数据库中查询到的记录封装成Java对象。下面是一个完整攻略: 1. Mybatis resultMap Mybatis提供了resultMap来将查询结果映射成Java对象。在mapper文件中定义resultMap: <resultMap id="userRe…

    other 2023年6月25日
    00
  • 解决Android Studio 出现“Cannot resolve symbol” 的问题

    当在Android Studio项目中遇到“Cannot resolve symbol”错误时,这通常意味着无法找到定义该符号的类、变量、方法或其他属性。这可能是由于多种原因引起的,下面是常见的几种原因及其解决方法: 1. 缺少依赖库 这通常是由于项目中缺少必要的依赖库而导致的。要解决这个问题,可以尝试以下几个步骤: 确认项目中是否导入所需的依赖库,在项目的…

    other 2023年6月26日
    00
  • dockerfilebuild镜像的构建环境(buildcontext)

    以下是关于Dockerfile构建镜像的构建环境(build context)的完整攻略,包括基本知识和两个示例说明。 基本知识 在使用Dockerfile构建镜像时,需要指定构建环境(build context)。构建环境是指Docker引擎在构建镜像时需要访问的文件和目录的集合。构建环境通常是一个目录,其中包含Docker和其他构建所需的文件。 示例说明…

    other 2023年5月7日
    00
  • u盘空间很足但提示文件过大无法复制的解决办法

    U盘空间很足但提示文件过大无法复制的解决办法攻略 如果你的U盘空间很足,但在复制文件时提示文件过大无法复制,可能是由于以下原因导致的:文件系统限制、文件大小超过U盘格式限制、文件系统错误等。下面是解决这个问题的完整攻略: 步骤一:检查文件系统限制 首先,右键点击U盘图标,选择“属性”。 在“属性”窗口中,查看“文件系统”一栏。常见的文件系统有FAT32和NT…

    other 2023年8月1日
    00
  • 总是听到有人说AndroidX,到底什么是AndroidX

    以下是关于AndroidX的详细攻略: AndroidX是什么? AndroidX是一个支持库的集合,旨在帮助开发者更轻松地构建Android应用。它提供了一组向后兼容的库,用于替代旧的Support库。AndroidX库的目标是提供更稳定、一致和功能丰富的开发体验。 AndroidX的优势 向后兼容性:AndroidX库提供了向后兼容的功能,使得开发者可以…

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