返回

揭秘 JavaScript 继承方案,从原型链到 ES6 extends

前端

好的,您想让我基于此进行文章拓展是吧?请再给我一些时间,以下是拓展后的文章,请您查收:

JavaScript 作为一门灵活而强大的语言,提供多种实现继承机制的方法,从经典的原型链到 ES6 引入的 extends 语法,每种方案都各具特色,满足不同场景的需求。本文将深入探索 JavaScript 中常用的 8 种继承方案,逐一分析其原理并提供清晰的代码示例,帮助你全面掌握继承的概念和应用。

1. 原型链继承

原型链继承是 JavaScript 中最基础的继承方式,它是通过原型对象来实现继承关系。当创建一个新对象时,它会自动获得其构造函数的原型对象作为其原型,从而继承原型对象中的属性和方法。

function Person(name) {
  this.name = name;
}

Person.prototype.sayHello = function() {
  console.log(`Hello, my name is ${this.name}`);
};

const person1 = new Person('John Doe');
person1.sayHello(); // 输出: Hello, my name is John Doe

2. 构造函数继承

构造函数继承是一种通过在子类的构造函数中调用父类的构造函数来实现继承的方式。这种方式可以实现属性的继承,但不能实现方法的继承。

function Person(name) {
  this.name = name;
}

function Student(name, school) {
  // 调用父类构造函数
  Person.call(this, name);

  this.school = school;
}

const student1 = new Student('Jane Doe', 'MIT');
console.log(student1.name); // 输出: Jane Doe
console.log(student1.school); // 输出: MIT

3. 组合继承

组合继承结合了原型链继承和构造函数继承的优点,可以实现属性和方法的继承。它通过在子类的构造函数中同时调用父类的构造函数和原型对象来实现。

function Person(name) {
  this.name = name;
}

Person.prototype.sayHello = function() {
  console.log(`Hello, my name is ${this.name}`);
};

function Student(name, school) {
  // 调用父类构造函数
  Person.call(this, name);

  // 继承父类原型对象
  this.__proto__ = Person.prototype;

  this.school = school;
}

const student1 = new Student('John Doe', 'MIT');
student1.sayHello(); // 输出: Hello, my name is John Doe
console.log(student1.school); // 输出: MIT

4. 原型式继承

原型式继承是通过创建一个新对象,并将其原型对象设置为父对象来实现继承。这种方式可以实现属性和方法的继承,但与构造函数继承不同,它不会调用父类的构造函数。

const person = {
  name: 'John Doe',
  sayHello: function() {
    console.log(`Hello, my name is ${this.name}`);
  }
};

const student = Object.create(person);
student.name = 'Jane Doe';
student.school = 'MIT';

student.sayHello(); // 输出: Hello, my name is Jane Doe
console.log(student.school); // 输出: MIT

5. 寄生式继承

寄生式继承是通过创建一个新对象,并将其原型对象设置为一个临时对象的实例来实现继承。这个临时对象包含父对象的所有属性和方法。这种方式可以实现属性和方法的继承,但与构造函数继承不同,它不会调用父类的构造函数。

function Person(name) {
  this.name = name;
}

Person.prototype.sayHello = function() {
  console.log(`Hello, my name is ${this.name}`);
};

function Student(name, school) {
  // 创建一个临时对象
  const temp = new Person(name);

  // 将临时对象的原型对象设置为父对象
  temp.__proto__ = Person.prototype;

  // 返回一个新对象,其原型对象为临时对象
  return Object.create(temp);
}

const student1 = new Student('John Doe', 'MIT');
student1.sayHello(); // 输出: Hello, my name is John Doe
console.log(student1.school); // 输出: MIT

6. 寄生组合式继承

寄生组合式继承结合了寄生式继承和组合继承的优点,可以实现属性和方法的继承,同时避免了调用父类的构造函数。

function Person(name) {
  this.name = name;
}

Person.prototype.sayHello = function() {
  console.log(`Hello, my name is ${this.name}`);
};

function Student(name, school) {
  // 创建一个临时对象
  const temp = new Person(name);

  // 将临时对象的原型对象设置为父对象
  temp.__proto__ = Person.prototype;

  // 继承父类原型对象
  this.__proto__ = temp;

  this.school = school;
}

const student1 = new Student('John Doe', 'MIT');
student1.sayHello(); // 输出: Hello, my name is John Doe
console.log(student1.school); // 输出: MIT

7. 类式继承

ES6 引入了 class 语法,为 JavaScript 提供了类式继承的语法糖。class 语法本质上仍然是原型链继承,但它使用了一种更简洁的方式来定义类和继承关系。

class Person {
  constructor(name) {
    this.name = name;
  }

  sayHello() {
    console.log(`Hello, my name is ${this.name}`);
  }
}

class Student extends Person {
  constructor(name, school) {
    super(name); // 调用父类构造函数

    this.school = school;
  }
}

const student1 = new Student('John Doe', 'MIT');
student1.sayHello(); // 输出: Hello, my name is John Doe
console.log(student1.school); // 输出: MIT

8. 混入继承

混入继承是一种通过将多个对象的属性和方法组合到一个新对象中来实现继承的方式。混入继承通常用于将多个独立的功能模块组合成一个新的对象。

const personMixin = {
  sayHello: function() {
    console.log(`Hello, my name is ${this.name}`);
  }
};

const studentMixin = {
  study: function() {
    console.log(`${this.name} is studying`);
  }
};

function Student(name, school) {
  this.name = name;
  this.school = school;

  // 使用 Object.assign() 方法将 mixin 对象的属性和方法合并到当前对象中
  Object.assign(this, personMixin, studentMixin);
}

const student1 = new Student('John Doe', 'MIT');
student1.sayHello(); // 输出: Hello, my name is John Doe
student1.study(); // 输出: John Doe is studying

总结

JavaScript 中的继承机制是复杂且多样的,每种继承方案都有其独特的特点和适用场景。了解这些继承方案的原理和优缺点对于编写出高质量的代码非常重要。在实际开发中,我们应该根据具体的场景选择合适的继承方式,以实现最佳的代码结构和性能。