面试高频题:js继承

52 阅读3分钟

在 JavaScript 中,继承是一种常见的模式,用来让一个对象可以共享另一个对象或类的属性和方法

1.原型链继承

通过修改子类的原型为父类的实例,从而继承父类的属性和方法。

function Parent() {
  this.name = "Parent";
}
Parent.prototype.sayHello = function () {
  console.log("Hello from Parent");
};

function Child() {}
Child.prototype = new Parent();

const child = new Child();
child.sayHello(); // Hello from Parent

优点:子类可以访问父类的所有方法和属性。

缺点:所有实例共享父类的引用属性(如数组或对象会被共享)。无法向父类构造函数传递参数。

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

在子类的构造函数中调用父类构造函数。

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

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

const child1 = new Child("Child1");
child1.colors.push("yellow");

const child2 = new Child("Child2");
console.log(child1.colors); // ["red", "blue", "green", "yellow"]
console.log(child2.colors); // ["red", "blue", "green"]

优点:不共享父类的引用属性。可以向父类构造函数传参。

缺点:只能继承父类实例的属性和方法,不能继承原型上的方法。

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

结合原型链和借用构造函数的优点,避免了它们的缺点。

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

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);
child1.colors.push('yellow');
console.log(child1.colors); // ['red', 'blue', 'green', 'yellow']
child1.sayName(); // 'Child1'

const child2 = new Child('Child2', 20);
console.log(child2.colors); // ['red', 'blue', 'green']
child2.sayName(); // 'Child2'

优点: 解决了原型链继承中引用类型共享的问题。子类实例可以独立拥有属性和方法。

缺点: 父类构造函数调用了两次:一次在 Parent.call,一次在 Child.prototype = new Parent(), 性能略有浪费。

4.原型式继承

通过 Object.create 方法以一个对象为原型创建另一个对象。

const parent = {
  name: "Parent",
  sayHello() {
    console.log("Hello from Parent");
  },
};

const child = Object.create(parent);
child.sayHello(); // Hello from Parent

优点:简单易用,性能较高。能够直接继承对象的属性和方法。

缺点:和原型链继承一样,共享引用类型的属性。

5.寄生式继承

在原型式继承的基础上,增强对象功能。

function createChild(parent) {
  const clone = Object.create(parent);
  clone.sayHi = function () {
    console.log("Hi from Child");
  };
  return clone;
}

const parent = {
  name: "Parent",
  sayHello() {
    console.log("Hello from Parent");
  },
};

const child = createChild(parent);
child.sayHi(); // Hi from Child

优点:灵活,可以动态扩展功能。

缺点:不支持原型链。

6.寄生组合式继承

通过寄生式继承的方式优化组合继承,避免多次调用父类构造函数。

function Parent(name) {
  this.name = name;
  this.colors = ["red", "blue", "green"];
}
Parent.prototype.sayHello = function () {
  console.log("Hello from Parent");
};

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

Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

const child = new Child("Child", 10);
child.sayHello(); // Hello from Parent

优点:不会多次调用父类构造函数。性能更优,避免组合继承中的效率问题。

缺点:实现稍微复杂。

7.ES6 Class 继承

通过 classextends 关键字实现继承。

class Parent {
  constructor(name) {
    this.name = name;
    this.colors = ["red", "blue", "green"];
  }
  sayHello() {
    console.log("Hello from Parent");
  }
}

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

const child = new Child("Child", 10);
child.sayHello(); // Hello from Parent
console.log(child.colors); // ["red", "blue", "green"]

优点:语法更加清晰简洁,符合现代 JavaScript 标准。支持父类和子类的更复杂操作。

缺点:语法糖,底层仍然基于原型实现。

总结

对于现代开发,ES6 的 Class 继承寄生组合式继承 是推荐的方式,尤其是 Class 继承具有语法上的优势,更容易被开发者接受。