您现在的位置是:网站首页 > 继承实现方式文章详情

继承实现方式

继承的概念

继承是面向对象编程的核心概念之一,允许一个对象基于另一个对象来创建。在JavaScript中,继承主要通过原型链实现,ES6之后也引入了基于class的语法糖。理解继承的实现方式对编写可维护、可扩展的代码至关重要。

原型链继承

原型链继承是最基础的继承方式,通过将子类的原型指向父类的实例来实现:

function Parent() {
  this.name = 'parent';
  this.colors = ['red', 'blue'];
}

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

function Child() {
  this.childProperty = 'child';
}

// 关键步骤:将Child的原型指向Parent的实例
Child.prototype = new Parent();

const child1 = new Child();
child1.sayName(); // 输出: parent

这种方式的缺点是:

  1. 所有子类实例共享父类实例的引用属性
  2. 无法向父类构造函数传参

构造函数继承

通过在子类构造函数中调用父类构造函数来解决原型链继承的问题:

function Parent(name) {
  this.name = name;
  this.colors = ['red', 'blue'];
}

function Child(name) {
  Parent.call(this, name); // 关键调用
  this.age = 10;
}

const child1 = new Child('child1');
child1.colors.push('green');

const child2 = new Child('child2');
console.log(child2.colors); // ['red', 'blue'] 不共享colors

优点是可以向父类传参且不共享引用属性,但缺点是父类原型上的方法不可访问。

组合继承

组合原型链继承和构造函数继承的优点:

function Parent(name) {
  this.name = name;
  this.colors = ['red', 'blue'];
}

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

function Child(name, age) {
  Parent.call(this, name); // 第二次调用Parent
  this.age = age;
}

Child.prototype = new Parent(); // 第一次调用Parent
Child.prototype.constructor = Child;

const child = new Child('Tom', 10);
child.sayName(); // Tom

这种方式虽然常用,但会调用两次父类构造函数,导致子类原型上有多余的属性。

原型式继承

基于已有对象创建新对象,类似Object.create:

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

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

const child = createObject(parent);
console.log(child.name); // parent

适用于不需要单独构造函数的场景,但同样存在引用属性共享的问题。

寄生式继承

在原型式继承基础上增强对象:

function createAnother(original) {
  const clone = Object.create(original);
  clone.sayHi = function() {
    console.log('hi');
  };
  return clone;
}

const parent = {
  name: 'parent'
};

const child = createAnother(parent);
child.sayHi(); // hi

这种方式适合主要关注对象而非自定义类型和构造函数的场景。

寄生组合式继承

目前最理想的继承方式,解决了组合继承的缺点:

function inheritPrototype(child, parent) {
  const prototype = Object.create(parent.prototype);
  prototype.constructor = child;
  child.prototype = prototype;
}

function Parent(name) {
  this.name = name;
  this.colors = ['red', 'blue'];
}

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

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

inheritPrototype(Child, Parent);

const child = new Child('Tom', 10);
child.sayName(); // Tom

这种方式只调用一次父类构造函数,保持原型链不变,是ES6 class继承的底层实现。

ES6 class继承

ES6引入了class语法糖,使继承更直观:

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

class Child extends Parent {
  constructor(name, age) {
    super(name); // 必须调用super
    this.age = age;
  }
  
  sayAge() {
    console.log(this.age);
  }
}

const child = new Child('Tom', 10);
child.sayName(); // Tom

class继承底层仍然是基于原型链,但语法更清晰。super关键字用于调用父类构造函数和方法。

多重继承的实现

JavaScript本身不支持多重继承,但可以通过混入(mixin)模式模拟:

class A {
  methodA() {
    console.log('A');
  }
}

class B {
  methodB() {
    console.log('B');
  }
}

function mixin(...classes) {
  return classes.reduce((base, mixin) => {
    Object.getOwnPropertyNames(mixin.prototype)
      .filter(name => name !== 'constructor')
      .forEach(name => {
        base.prototype[name] = mixin.prototype[name];
      });
    return base;
  }, class {});
}

class C extends mixin(A, B) {
  methodC() {
    console.log('C');
  }
}

const c = new C();
c.methodA(); // A
c.methodB(); // B

继承与性能考虑

原型链查找会影响性能,过深的原型链会导致查找时间增加。现代JavaScript引擎优化了原型查找,但仍需注意:

// 不推荐的深层继承链
function A() {}
function B() { A.call(this); }
function C() { B.call(this); }
function D() { C.call(this); }
// ...更多层级

// 更扁平的结构通常性能更好
class Base {
  commonMethod() {}
}

class Feature1 extends Base {
  method1() {}
}

class Feature2 extends Base {
  method2() {}
}

继承与组合的对比

在复杂场景下,组合可能比继承更灵活:

// 组合示例
const canEat = {
  eat() {
    console.log('Eating');
  }
};

const canWalk = {
  walk() {
    console.log('Walking');
  }
};

function Person() {
  // 组合多个行为
  Object.assign(this, canEat, canWalk);
}

const person = new Person();
person.eat();
person.walk();

组合模式避免了继承的层级问题,更易于维护和扩展。

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步