JavaScript的六种继承方式

藏宝库编辑 2024-9-11 10:28:20 91 0 来自 中国
JavaScript的几种继承方式


  • 原型链继承
  • 借助构造函数继承(经典继承)
  • 组合继承:原型链 + 借用构造函数(最常用)
  • 原型式继承 (Object.create)
  • 寄生式继承
  • 寄生组合式继承(最理想)
ES6中的继承

1. 原型链继承

子范例的原型为父范例的一个实例对象
function Parent() {    this.name = 'bigStar';    this.colors = ['red', 'blue', 'yellow'];}Parent.prototype.getName = function() {    console.log(this.name)}function Child() {    this.subName = 'litterStar';}// 焦点代码: 子范例的原型为父范例的一个实例对象Child.prototype = new Parent();let child1 = new Child();let child2 = new Child();child1.getName(); // bigStarchild1.colors.push('pink');// 修改 child1.colors 会影响 child2.colorsconsole.log(child1.colors); // [ 'red', 'blue', 'yellow', 'pink' ]console.log(child2.colors); // [ 'red', 'blue', 'yellow', 'pink' ]注意焦点代码: Child.prototype = new Parent();

  • 特点:
    父类新增在构造函数上面的方法,子类都能访问到
  • 缺点:
    来自原型对象的所有属性被所有实例共享,child1修改 colors 会影响child2的 colors
    创建子类实例时,无法向父类的构造函数传参
2.借助构造函数继承(经典继承)

在子类的构造函数中利用 call()或者 apply() 调用父范例构造函数
function Parent(name) {    this.name = name;    this.colors = ['red', 'blue', 'yellow'];}Parent.prototype.getName = function() {    console.log(this.name)}function Child(name, age) {    // 焦点代码:“借调”父范例的构造函数    Parent.call(this, name);    this.age = age;}let child1 = new Child('litterStar');let child2 = new Child('luckyStar');console.log(child1.name); // litterStarconsole.log(child2.name); // luckyStar// 这种方式只是实现部分的继承,假如父类的原型另有方法和属性,子类是拿不到这些方法和属性的。child1.getName(); // TypeError: child1.getName is not a function注意焦点代码: Parent.call(this, name);

  • 特点:
    克制引用范例的属性被所有实例共享
    创建子类实例时,可以向父类通报参数
  • 缺点
    实例并不是父类的实例,只是子类的实例
    只能继承父类的实例属性和方法,不能继承原型属性和方法
    无法实现函数复用,每次创建实例都会创建一遍方法,影响性能
3.组合继承:原型链 + 借用构造函数(最常用)

function Parent(name) {    this.name = name;    this.colors = ['red', 'blue', 'yellow'];}Parent.prototype.getName = function() {    console.log(this.name)}function Child(name, age) {    // 焦点代码①    Parent.call(this, name);    this.age = age;}// 焦点代码②: 子范例的原型为父范例的一个实例对象Child.prototype = new Parent();Child.prototype.constructor = Child;// 可以通过子类给父类的构造函数传参let child1 = new Child('litterStar');let child2 = new Child('luckyStar');child1.getName(); // litterStarchild2.getName(); // luckyStarchild1.colors.push('pink');// 修改 child1.colors 不会影响 child2.colorsconsole.log(child1.colors); // [ 'red', 'blue', 'yellow', 'pink' ]console.log(child2.colors); // [ 'red', 'blue', 'yellow' ]注意焦点代码: Parent.call(this, name);和 Child.prototype = new Parent();

  • 特点
    融合了原型链继承和借用构造函数的长处,称为JavaScript中最常用的继承模式。
  • 缺点
    调用了两次父类构造函数,天生了两份实例
    一次是设置子范例实例的原型的时间 Child.prototype = new Parent();
    一次是创建子范例实例的时间 let child1 = new Child('litterStar');, 调用 new 会实行 Parent.call(this, name);,此时会再次调用一次 Parent构造函数
4. 原型式继承 (Object.create)

借助原型可以基于现有方法来创建对象,var B = Object.create(A) 以A对象为原型,天生A对象,B继承了A的所有属性和方法。
const person = {    name: 'star',    colors: ['red', 'blue'],}// 焦点代码:Object.createconst person1 = Object.create(person);const person2= Object.create(person);person1.name = 'litterstar';person2.name = 'luckystar';person1.colors.push('yellow');console.log(person1.colors); // [ 'red', 'blue', 'yellow' ]console.log(person2.colors); // [ 'red', 'blue', 'yellow' ]注意焦点代码: const person1 = Object.create(person);

  • 特点
    没有严酷意义上的构造函数,借助原型可以基于已有对象创建新对象
  • 缺点
    来自原型对象的所有属性被所有实例共享,person1修改 colors 会影响person2的 colors,这点跟原型链继承一样。
5. 寄生式继承

创建一个用于封装继承过程的函数,该函数在内部以某种方式来加强对象
function createObj (original) {    // 通过调用函数创新一个新对象    var clone = Object.create(original);    // 以某种方式来加强这个对象    clone.sayName = function () {        console.log('hi');    }    // 返回这个对象    return clone;}

  • 缺点: 每次创建对象都会创建一遍方法,跟借助构造函数模式一样
6.寄生组合式继承(最理想的)

我们可以先回忆一下JavaScript最常用的继承模式: 组合继承(原型链 + 借用构造函数),它的最大缺点是会调用两次父构造函数(Child.prototype = new Parent();和 let child1 = new Child('litterStar');)。
我们是否可以想办法是调用一次?可以让 Child.prototype访问到 Parent.prototype。
我们不能直接利用 Child.prototype = Parent.prototype来实现,由于会出现一些副作用,你大概在修改 Child.prototype的时间会修改Parent.prototype。
可以利用 Object.create(...)来实现
Object.create MDN上的解释:它会创建一个新对象,利用现有的对象来提供新创建的对象的__proto__
function Parent(name) {    this.name = name;    this.colors = ['red', 'blue', 'yellow'];}Parent.prototype.getName = function() {    console.log(this.name)}function Child(name, age) {    // 焦点代码①    Parent.call(this, name);    this.age = age;}// 焦点代码②Child.prototype = Object.create(Parent.prototype);Child.prototype.constructor = Child;注意焦点代码: Parent.call(this, name);和 Child.prototype = Object.create(Parent.prototype);
寄生组合式继承,集寄生式继承和组合式继承的长处,是引用范例最理想的继承范式。
7. ES6 中class的继承

ES6中引入了class关键字,可以通过extends关键字实现继承。
class Parent {}class Child extends Parent {    constructor(name, age, color) {        // 调用父类的constructor(name, age)        super(name, age);        this.color = color;    }    toString() {        return this.color + ' ' + super.toString(); // 调用父类的toString()    }}class关键字只是原型的语法糖,JavaScript继承仍旧是基于原型实现的。
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-10-18 20:27, Processed in 0.155384 second(s), 32 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表