js各种继承方式汇总

js中的各种继承实现汇总

首先定义一个父类:

function Animal(name) {
  this.name = name || '动物'
  this.sleep = function () {
    console.log(this.name + '正在睡觉!')
  }
}
Animal.prototype.eat = function (food) {
  console.log(this.name + '正在吃:' + food)
}

原型链继承

特点: 1、子类的原型指向父类的实例 缺点: 1、无法多继承 2、无法向父类的构造传参 3、来自原型对象的引用属性是所有实例共享的

function Cat() { }
Cat.prototype = new Animal()
Cat.prototype.name = '猫'

// Test code
const cat = new Cat()
console.log(cat.name)   // 猫
cat.eat('鱼')       // 猫正在吃:鱼
cat.sleep()       // 猫正在睡觉!
console.log(cat instanceof Cat)   // true
console.log(cat instanceof Animal)    // true

构造继承(使用call、apply方式)

特点: 1、子类的构造中进行父类构造的调用 优点: 1、实现了多继承,想继承哪个直接在子类构造里面call或者apply哪个就行 2、避免所有子类实例共享父类引用属性问题 3、创建子类实例时,可以向父类传递参数 缺点: 1、没用到原型,只是单纯继承了父类的实例属性及方法 2、没继承原型上的属性及方法 3、每个子类都有父类方法属性的副本,影响性能,没有实现父类函数复用

function Dog(name) {
  Animal.call(this)
  this.name = name || '狗'
}
// Test code
const dog = new Dog()
console.log(dog.name)   // 狗
dog.sleep()   // 狗正在睡觉!
// dog.eat('粑粑')  不能继承原型上的eat
console.log(dog instanceof Dog)   // true
console.log(dog instanceof Animal)   // false,等于是复制父类的实例属性给子类,没用到原型

实例继承

特点: 1、子类的构造中返回父类的实例 优点: 1、可以继承原型上的属性或方法 缺点: 1、实例为父类实例,而非子类实例 2、不能实现多继承

function Pig(name) {
  const instance = new Animal()
  instance.name = name || '猪'
  return instance
}
// Test code
const pig = new Pig()
console.log(pig.name)   // 猪
pig.sleep()   // 猪正在睡觉!
pig.eat('菠菜叶子')   // 猪正在吃:菠菜叶子
console.log(pig instanceof Pig)   // false
console.log(pig instanceof Animal)   // true

复制继承或拷贝继承(暴力继承)

特点: 1、子类的构造中强制拷贝父类原型上的属性或方法 优点: 1、可以多重继承 缺点: 1、效率较低,内存占用高 2、不能继承父类不可枚举的属性(不能用for in遍历的)

function Rabbit(name) {
  const animal = new Animal()   // 多重继承直接new多个遍历
  for (let i in animal) {
    Rabbit.prototype[i] = animal[i]
  }
  Rabbit.prototype.name = name || '兔子'
}
// Test code
const rabbit = new Rabbit('傻兔子')
console.log(rabbit.name)    // 傻兔子
rabbit.sleep()   // 傻兔子正在睡觉!
rabbit.eat('胡萝卜')   // 傻兔子正在吃:胡萝卜
console.log(rabbit instanceof Rabbit)   // true
console.log(rabbit instanceof Animal)   // false

对象冒充继承(类似于构造继承)

同构造继承

function Mouse(name) {
  this.method = Animal
  this.method(name)
  delete this.method
}
// Test code
const mouse = new Mouse('老鼠')
console.log(mouse.name)    // 老鼠
mouse.sleep()   // 老鼠正在睡觉!
// mouse.eat('大米')   // 继承不到原型上的属性
console.log(mouse instanceof Mouse)   // true
console.log(mouse instanceof Animal)   // false

组合继承(构造继承+原型链继承)

特点: 1、组合构造继承和原型链继承 优点: 1、可以继承实例属性/方法,也可以继承原型属性/方法 2、既是子类的实例,也是父类的实例 3、不存在引用属性共享问题 4、可传参 5、函数可复用 缺点: 1、调用了两次父类构造函数

function Duck(name) {
  Animal.call(this, name)
}
Duck.prototype = new Animal()
Duck.prototype.say = function () {   // 新增的say方法
  console.log(this.name + '正在说话!')
}
// Test code
const duck = new Duck('鸭子')
console.log(duck.name)    // 鸭子
duck.sleep()   // 鸭子正在睡觉!
duck.eat('虫子')   // 鸭子正在吃:虫子
console.log(duck instanceof Duck)   // true
console.log(duck instanceof Animal)   // true

寄生组合继承

特点: 1、使用中间函数对象避免父类构造被两次调用的问题 优点: 1、完美 缺点: 1、写起来费劲

function Snake(name) {
  Animal.call(this, name)
}
const Super = function () { }
Super.prototype = Animal.prototype
Snake.prototype = new Super()
// Test code
const snake = new Snake('蛇')
console.log(snake.name)    // 蛇
snake.sleep()   // 蛇正在睡觉!
snake.eat('小鸟')   // 蛇正在吃:小鸟
console.log(snake instanceof Snake)   // true
console.log(snake instanceof Animal)   // true

ES6的class继承

class Point {
  constructor(x, y) {  //constructor 构造方法
    this.x = x
    this.y = y
  }
  toString() {
    return this.x + ', ' + this.y
  }
  static hello() {
    console.log('hello world!')
  }
}
class ColorPoint extends Point {
  constructor(x, y, color){
    super(x, y)   // 必须先调用super,否则子类的this不可用
    this.color = color
  }
  toString() {
    return this.color + ',' + super.toString()
  }
}
// Test code
const cp = new ColorPoint(25, 8, 'green')
console.log(cp instanceof ColorPoint) // true
console.log(cp instanceof Point) // true
ColorPoint.hello()    // hello world!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏python3

python3--object类,继承与派生,super方法,钻石继承问题

{'kind': '波斯猫', 'name': '小黑', 'sex': '公'}

3531
来自专栏PhpZendo

带你入门 JavaScript ES6 (四)

使用类声明是,需要先声明类,然后才能访问,否则抛出ReferenceError。这一点不同于函数声,函数声明会提升作用域,而无需事先声明

741
来自专栏恰童鞋骚年

你必须知道的指针基础-7.void指针与函数指针

  void *表示一个“不知道类型”的指针,也就不知道从这个指针地址开始多少字节为一个数据。和用int表示指针异曲同工,只是更明确是“指针”。

2452
来自专栏木子昭的博客

Javascript实现完美继承实现javascript完美继承要考虑三个方面:

? 实现javascript完美继承要考虑三个方面: 第一步: 获取父构造函数体内的属性 解决方法: 通过 Father.call(this)实现(这里的t...

4306
来自专栏ImportSource

为什么实现了equals()就必须实现hashCode()?

我们先来看下面这个简单的例子,然后运行她: class Person{ private String name; private int age; ...

3964
来自专栏Java编程

Java习惯用法总结

在Java编程中,有些知识 并不能仅通过语言规范或者标准API文档就能学到的。在本文中,我会尽量收集一些最常用的习惯用法,特别是很难猜到的用法。(Joshua ...

1.1K3
来自专栏ios 技术积累

Java 多态

●消除类型之间的耦合关系 ●可替换性 ●可扩充性 ●接口性 ●灵活性 ●简化性

2292
来自专栏Java技术栈

equals 和 hashCode 到底有什么联系?一文告诉你!

Java的基类Object提供了一些方法,其中equals()方法用于判断两个对象是否相等,hashCode()方法用于计算对象的哈希码。equals()和ha...

1143
来自专栏菩提树下的杨过

python面向对象笔记

一、封装(属性/私有方法/公有方法/静态方法/构造函数...) # 定义一个类 class Animal: # 私有成员(用_开头的约定为私有成员 - ...

2793
来自专栏xingoo, 一个梦想做发明家的程序员

【设计模式】—— 解释器模式Interpret

  模式意图   自定义某种语言后,给定一种文法标准,定义解释器,进行解析。   做过搜索的朋友们可能更了解一些,平时我们搜索所需要的词库,通常就需要用这种方...

2146

扫码关注云+社区

领取腾讯云代金券