JavaScript继承方式全解析

1. 原型链继承

原理:通过修改子类的原型(prototype)为父类的实例,实现属性和方法的继承。

示例

javascript

function Parent() {
  this.name = 'parent';
  this.hobbies = ['reading'];
}

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

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

// 核心:将Child的原型指向Parent的实例
Child.prototype = new Parent();
Child.prototype.constructor = Child; // 修复构造函数指向

const child1 = new Child();
const child2 = new Child();
child1.hobbies.push('swimming');
console.log(child2.hobbies); // ['reading', 'swimming'](被修改)

优点

  • 实现简单,符合原型链的自然继承逻辑。
  • 父类原型上的方法可被所有子类实例共享,节省内存。

缺点

  • 引用类型属性共享问题:父类实例的引用类型属性(如数组、对象)会被所有子类实例共享,修改一个实例会影响其他实例。
  • 无法向父类传参:创建子类实例时,无法为父类构造函数传递参数。
  • 原型污染风险:直接修改原型可能影响所有实例。

2. 构造函数继承(借用构造函数)

原理:在子类构造函数中通过 call() 或 apply() 调用父类构造函数,将父类的属性 "复制" 到子类实例中。

示例

javascript

function Parent(name) {
  this.name = name;
  this.hobbies = ['reading'];
}

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

function Child(name, age) {
  Parent.call(this, name); // 借用父类构造函数
  this.age = age;
}

const child = new Child('child', 18);
console.log(child.name); // 'child'
console.log(child.hobbies); // ['reading']
console.log(child.sayName); // undefined(无法继承原型方法)

优点

  • 解决引用类型共享问题:每个子类实例都有独立的属性副本。
  • 可向父类传参:创建子类时可灵活初始化父类属性。

缺点

  • 无法继承父类原型方法:子类只能继承父类构造函数中的属性,无法访问父类原型上的方法,导致方法无法复用。
  • 内存浪费:每个子类实例都有独立的方法副本,造成冗余。

3. 组合继承(原型链 + 构造函数)

原理:结合原型链和构造函数继承的优点:用原型链继承父类原型方法,用构造函数继承父类实例属性。

示例

javascript

function Parent(name) {
  this.name = name;
  this.hobbies = ['reading'];
}

Parent.prototype.sayName = function() {
  console.log(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.hobbies.push('swimming');
console.log(child2.hobbies); // ['reading'](未被修改)
child1.sayName(); // 'child1'

优点

  • 既继承了父类原型方法(共享复用),又避免了引用类型属性共享问题。
  • 可向父类构造函数传参,灵活性高。

缺点

  • 父类构造函数被调用两次:第一次是 new Parent() 创建原型对象时,第二次是子类构造函数中 Parent.call(this)。这会导致原型对象和子类实例中都存在父类的属性副本,造成冗余。

4. 寄生组合继承

原理:通过创建一个空构造函数作为中间层,避免组合继承中父类构造函数的重复调用。

示例

javascript

function Parent(name) {
  this.name = name;
  this.hobbies = ['reading'];
}

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

function Child(name, age) {
  Parent.call(this, name); // 继承实例属性
  this.age = age;
}

// 核心:创建空构造函数作为中间层,避免重复调用Parent
function createPrototype(SuperType) {
  function F() {}
  F.prototype = SuperType.prototype;
  return new F();
}

Child.prototype = createPrototype(Parent);
Child.prototype.constructor = Child;

const child = new Child('child', 18);
console.log(child instanceof Parent); // true

优点

  • 避免重复调用父类构造函数:仅继承父类原型方法,不创建父类实例属性。
  • 性能最优:是现代 JavaScript 中常用的继承方式。

缺点

  • 代码复杂度较高,需要手动修复 constructor 指向。

5. ES6 Class 继承

原理:使用 class 和 extends 关键字实现继承,本质上是寄生组合继承的语法糖。

示例

javascript

class Parent {
  constructor(name) {
    this.name = name;
    this.hobbies = ['reading'];
  }

  sayName() {
    console.log(this.name);
  }
}

class Child extends Parent {
  constructor(name, age) {
    super(name); // 调用父类构造函数
    this.age = age;
  }

  sayAge() {
    console.log(this.age);
  }
}

const child = new Child('child', 18);
console.log(child instanceof Parent); // true
child.sayName(); // 'child'

优点

  • 语法简洁:符合面向对象编程范式,代码可读性高。
  • 支持 super 关键字:方便访问父类方法和属性。
  • 内置类型继承:可直接继承 ArrayMap 等内置类。

缺点

  • 依赖 ES6 环境:需兼容旧版浏览器时需使用 Babel 转译。
  • 原理复杂:内部仍依赖原型链和构造函数机制。

6. 混入(Mixin)继承

原理:通过合并多个对象的属性和方法实现 "多继承"。

示例

javascript

const Flyable = {
  fly() {
    console.log('I can fly');
  }
};

const Swimmable = {
  swim() {
    console.log('I can swim');
  }
};

class Bird {
  constructor() {
    this.name = 'bird';
  }
}

// 将Flyable和Swimmable的方法混入Bird
Object.assign(Bird.prototype, Flyable, Swimmable);

const bird = new Bird();
bird.fly(); // 'I can fly'
bird.swim(); // 'I can swim'

优点

  • 灵活组合功能:可按需合并多个对象的属性和方法。
  • 实现多继承:JavaScript 原生不支持多继承,混入是一种替代方案。

缺点

  • 命名冲突风险:多个混入对象可能包含同名方法,导致覆盖。
  • 破坏封装性:混入对象的内部状态可能被外部访问和修改。

总结对比

继承方式优点缺点适用场景
原型链继承实现简单,共享原型方法引用类型共享,无法传参简单继承,无引用类型属性
构造函数继承避免引用类型共享,可传参无法继承原型方法仅需继承实例属性
组合继承继承完整,无共享问题父类构造函数调用两次需继承完整功能
寄生组合继承性能最优,避免重复调用代码复杂高性能场景
ES6 Class 继承语法简洁,支持 super依赖 ES6 环境现代 JavaScript 开发
混入继承灵活组合多对象功能,实现多继承命名冲突,破坏封装性需要多继承或功能组合的场景

推荐:在现代 JavaScript 中,优先使用 ES6 Class 继承 或 寄生组合继承,它们在性能和可维护性上表现最佳。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值