探究js继承实现方式-js面向对象的基础

继承

  • 是什么
    • 继承是面向对象三大特性:继承、封装、多态 之一
    • 具体来说就是让子类可以访问到父类的方法
  • 实现方式有?
    • 原型链继承
    • 构造函数继承
    • 组合继承
    • 原型式继承
    • 寄生式继承
    • 寄生组合式继承
    • 类的继承

原型链继承

  • 将父类的实例作为子类的原型来实现继承
  • 缺点:多个实例对象共享一个原型对象
TypeScript 复制代码
// 原型链继承
function Parent() {
  this.name = 'parent';
  this.colors = ['red', 'blue', 'green'];
}

Parent.prototype.getName = function() {
  return this.name;
};

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

// 关键:将父类的实例作为子类的原型
Child.prototype = new Parent();
Child.prototype.constructor = Child; // 修复constructor指向

// 测试
const child1 = new Child();
const child2 = new Child();
console.log(child1.getName()); // parent
child1.colors.push('black');
console.log(child1.colors); // ['red', 'blue', 'green', 'black']
console.log(child2.colors); // ['red', 'blue', 'green', 'black'] - 引用类型被所有实例共享

构造函数继承

  • 在子类构造函数中调用父类构造函数来实现属性继承
  • 缺点:无法继承父类原型上的方法
TypeScript 复制代码
// 构造函数继承
function Parent() {
  this.name = 'parent';
  this.colors = ['red', 'blue', 'green'];
}

Parent.prototype.getName = function() {
  return this.name;
};

function Child() {
  // 关键:调用父类构造函数
  Parent.call(this);
  this.age = 18;
}

// 测试
const child1 = new Child();
const child2 = new Child();
child1.colors.push('black');
console.log(child1.colors); // ['red', 'blue', 'green', 'black']
console.log(child2.colors); // ['red', 'blue', 'green'] - 引用类型不共享
console.log(child1.getName); // undefined - 无法继承原型方法

组合继承

  • 利用原型继承和构造函数继承的特点,刚好可以解决他们的缺点
  • 使用父类构造函数继承属性
  • 设置子构造函数原型为父构造函数实例,继承原型
  • 缺点:父类构造函数重复执行两次,导致实例对象和原型上都有属性,造成浪费
TypeScript 复制代码
// 组合继承
function Parent(name) {
  this.name = name;
  this.colors = ['red', 'blue', 'green'];
}

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;

// 测试
const child1 = new Child('child1', 18);
const child2 = new Child('child2', 20);
child1.colors.push('black');
console.log(child1.colors); // ['red', 'blue', 'green', 'black']
console.log(child2.colors); // ['red', 'blue', 'green']
console.log(child1.getName()); // child1
console.log(child2.getName()); // child2

原型式继承

  • 利用object.create继承对象
  • 缺点:多个实例对象共享一个原型对象
TypeScript 复制代码
const parent = {
  name: "parent",
  colors: ["red", "blue", "green"],
  getName: function () {
    return this.name;
  },
};

const  child1 = Object.create(parent)
const  child2 = Object.create(parent)

// 等同于 Object
// const  child1 = Object(parent)
// const  child2 = Object(parent)

console.log(child1.getName(), child1.colors); // parent [ 'red', 'blue', 'green' ]
console.log(child2.getName(), child2.colors); // parent [ 'red', 'blue', 'green' ]

child1.name = "child1";
child1.colors.push("black");

child2.name = "child2";

console.log(child1.getName(), child1.colors); // child1 [ 'red', 'blue', 'green', 'black' ]
console.log(child2.getName(), child2.colors); // child2 [ 'red', 'blue', 'green', 'black' ]

寄生式继承

  • 原型式继承的基础上添加方法,增强对象
JavaScript 复制代码
// 寄生式继承
function createAnother(original) {
  const clone = Object.create(original); // 创建一个新对象
  clone.sayHi = function() { // 增强这个对象
    console.log('hi');
  };
  return clone; // 返回这个对象
}

const parent = {
  name: 'parent',
  colors: ['red', 'blue', 'green']
};

const child = createAnother(parent);
child.sayHi(); // hi
console.log(child.colors); // ['red', 'blue', 'green']

寄生组合式继承

  • 结合了组合继承和寄生继承的优点
  • 解决了继承的所有问题,是最优解
JavaScript 复制代码
// 组合继承
function Parent(name) {
  this.name = name;
  this.colors = ["red", "blue", "green"];
}

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的原型,如果后续修改Child的原型不会影响Parent的原型
Child.prototype.constructor = Child;

// 测试
const child1 = new Child("child1", 18);
const child2 = new Child("child2", 20);
child1.colors.push("black");
console.log(child1.colors); // ['red', 'blue', 'green', 'black']
console.log(child2.colors); // ['red', 'blue', 'green']
console.log(child1.getName()); // child1
console.log(child2.getName()); // child2

类的继承

  • ES6推出了class关键字,可以像一般面向对象语言一样使用class关键字定义对象,但本质上class是一种语法糖,底层还是使用原型实现的实例对象和继承
  • 实现的效果和寄生组合式继承一样,实例属性不会相互影响
JavaScript 复制代码
class Parent {
  constructor() {
    this.name = "parent";
    this.colors = ["red", "blue", "green"];
  }

  getName() {
    return this.name;
  }
}

class Child extends Parent {
  constructor(name) {
    super(); // 继承时必须调用父类构造函数
    this.name = name;
  }

  // 静态方法,相当于直接在Child构造函数上挂方法 Child.sayHi
  static sayHi() {
    console.log("hi child");
  }
}

const child1 = new Child('child1', 18);
const child2 = new Child('child2', 20);
child1.colors.push('black');
console.log(child1.colors); // ['red', 'blue', 'green', 'black']
console.log(child2.colors); // ['red', 'blue', 'green']
console.log(child1.getName()); // child1
console.log(child2.getName()); // child2
相关推荐
Mike_jia14 小时前
DBSyncer:开源数据同步中间件全景实战指南
前端
烛阴14 小时前
从`new`关键字开始:精通C#类与对象
前端·c#
天若有情67314 小时前
笑喷!乌鸦哥版demo函数掀桌怒怼主函数:难办?那就别办了!
java·前端·servlet
记得记得就15114 小时前
【Nginx 实战系列(一)—— Web 核心概念、HTTP/HTTPS协议 与 Nginx 安装】
前端·nginx·http
天蓝色的鱼鱼14 小时前
mescroll老用户亲测z-paging:这些功能让我果断切换!
前端·uni-app
JarvanMo14 小时前
适用于 iOS 开发者的 Flutter 3.38:Xcode 与 Swift 集成新技巧
前端
北极糊的狐15 小时前
Vue 中 vue-awesome-swiper的使用笔记(适配 Vue2/Vue3)
前端·javascript·vue.js
anyup15 小时前
🔥100+ 天,已全面支持鸿蒙!uView Pro 近期更新盘点及未来计划
前端·uni-app·harmonyos
2***574215 小时前
前端数据可视化应用
前端·信息可视化
xiaoxue..15 小时前
栈的全面解析:ADT、实现与应用
javascript·数据结构·面试