Skip to content

class 语法糖背后的原型链操作:extends 如何实现继承?

理解 ES6 Class 的本质

ES6 的 class 语法是 JavaScript 原型继承机制的语法糖。虽然它看起来像其他面向对象语言中的类,但实际上它仍然基于 JavaScript 的原型链机制。理解 class 语法背后的原型操作对于深入掌握 JavaScript 面向对象编程至关重要。

ES5 中的构造函数和原型

javascript
// ES5 中的构造函数和原型
function PersonES5(name, age) {
  this.name = name;
  this.age = age;
}

PersonES5.prototype.greet = function() {
  return `Hello, I'm ${this.name}`;
};

PersonES5.prototype.getAge = function() {
  return this.age;
};

// 创建实例
const person1 = new PersonES5('Alice', 30);
console.log(person1.greet()); // Hello, I'm Alice

// 检查原型链
console.log(person1.__proto__ === PersonES5.prototype); // true
console.log(PersonES5.prototype.constructor === PersonES5); // true

ES6 Class 语法

javascript
// ES6 Class 语法
class PersonES6 {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  
  greet() {
    return `Hello, I'm ${this.name}`;
  }
  
  getAge() {
    return this.age;
  }
}

// 创建实例
const person2 = new PersonES6('Bob', 25);
console.log(person2.greet()); // Hello, I'm Bob

// 检查原型链
console.log(person2.__proto__ === PersonES6.prototype); // true
console.log(PersonES6.prototype.constructor === PersonES6); // true

Class 与原型链的关系

Class 方法存储在原型上

javascript
class MyClass {
  constructor(value) {
    this.value = value;
  }
  
  method() {
    return this.value;
  }
  
  static staticMethod() {
    return 'static';
  }
}

const instance = new MyClass(42);

// 实例方法存储在原型上
console.log(instance.method === MyClass.prototype.method); // true
console.log(instance.hasOwnProperty('method')); // false

// 静态方法存储在构造函数上
console.log(MyClass.staticMethod === MyClass.constructor.staticMethod); // false
console.log(MyClass.staticMethod()); // static

// 检查原型链
console.log(instance.__proto__ === MyClass.prototype); // true
console.log(MyClass.prototype.constructor === MyClass); // true

Class 的内部属性

javascript
class Example {
  constructor(name) {
    this.name = name;
  }
  
  // 实例方法
  instanceMethod() {
    return `Instance: ${this.name}`;
  }
  
  // 静态方法
  static staticMethod() {
    return 'Static method';
  }
  
  // getter
  get displayName() {
    return this.name.toUpperCase();
  }
  
  // setter
  set displayName(value) {
    this.name = value.toLowerCase();
  }
}

const example = new Example('Test');

// 检查各种属性的位置
console.log(example.hasOwnProperty('name')); // true
console.log(example.hasOwnProperty('instanceMethod')); // false
console.log(example.__proto__.hasOwnProperty('instanceMethod')); // true
console.log(Example.hasOwnProperty('staticMethod')); // true
console.log(example.__proto__.hasOwnProperty('displayName')); // true

extends 继承的实现机制

基本继承

javascript
// 父类
class Animal {
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    return `${this.name} makes a sound`;
  }
  
  static getSpecies() {
    return 'Animal';
  }
}

// 子类
class Dog extends Animal {
  constructor(name, breed) {
    super(name); // 调用父类构造函数
    this.breed = breed;
  }
  
  speak() {
    return `${super.speak()}, specifically barking`;
  }
  
  fetch() {
    return `${this.name} fetches the ball`;
  }
}

const dog = new Dog('Buddy', 'Golden Retriever');
console.log(dog.speak()); // Buddy makes a sound, specifically barking
console.log(dog.fetch()); // Buddy fetches the ball
console.log(Dog.getSpecies()); // Animal

// 检查原型链
console.log(dog.__proto__ === Dog.prototype); // true
console.log(Dog.prototype.__proto__ === Animal.prototype); // true
console.log(Animal.prototype.__proto__ === Object.prototype); // true

extends 背后的原型链操作

javascript
// 手动实现 extends 的原型链设置
function Animal(name) {
  this.name = name;
}

Animal.prototype.speak = function() {
  return `${this.name} makes a sound`;
};

Animal.getSpecies = function() {
  return 'Animal';
};

// 手动实现继承
function Dog(name, breed) {
  Animal.call(this, name); // 相当于 super(name)
  this.breed = breed;
}

// 设置原型链 (相当于 extends)
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

// 添加子类方法
Dog.prototype.speak = function() {
  const parentSpeak = Animal.prototype.speak.call(this);
  return `${parentSpeak}, specifically barking`;
};

Dog.prototype.fetch = function() {
  return `${this.name} fetches the ball`;
};

// 设置静态方法继承
Object.setPrototypeOf(Dog, Animal);

const dog2 = new Dog('Max', 'Labrador');
console.log(dog2.speak()); // Max makes a sound, specifically barking
console.log(Dog.getSpecies()); // Animal

super 关键字的实现机制

super 在构造函数中的使用

javascript
class Parent {
  constructor(name) {
    this.name = name;
    console.log('Parent constructor called');
  }
}

class Child extends Parent {
  constructor(name, age) {
    console.log('Before super() call');
    super(name); // 必须在使用 this 之前调用
    console.log('After super() call');
    this.age = age;
  }
}

const child = new Child('Alice', 10);
// 输出顺序:
// Before super() call
// Parent constructor called
// After super() call

super 在方法中的使用

javascript
class Rectangle {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }
  
  get area() {
    return this.width * this.height;
  }
  
  toString() {
    return `Rectangle(${this.width}x${this.height})`;
  }
}

class Square extends Rectangle {
  constructor(side) {
    super(side, side); // 调用父类构造函数
  }
  
  toString() {
    return `Square(${this.width}) - ${super.toString()}`; // 调用父类方法
  }
  
  set side(value) {
    super.width = value; // 调用父类 setter(如果存在)
    super.height = value;
  }
}

const square = new Square(5);
console.log(square.area); // 25
console.log(square.toString()); // Square(5) - Rectangle(5x5)

Class 继承的高级特性

静态方法继承

javascript
class Base {
  static baseMethod() {
    return 'Base static method';
  }
  
  static get baseProperty() {
    return 'Base static property';
  }
}

class Derived extends Base {
  static derivedMethod() {
    return 'Derived static method';
  }
  
  static callBaseMethod() {
    return super.baseMethod(); // 调用父类静态方法
  }
}

console.log(Derived.baseMethod()); // Base static method
console.log(Derived.baseProperty); // Base static property
console.log(Derived.derivedMethod()); // Derived static method
console.log(Derived.callBaseMethod()); // Base static method

// 检查原型链
console.log(Derived.__proto__ === Base); // true
console.log(Derived.prototype.__proto__ === Base.prototype); // true

getter 和 setter 的继承

javascript
class Temperature {
  constructor(celsius) {
    this._celsius = celsius;
  }
  
  get celsius() {
    return this._celsius;
  }
  
  set celsius(value) {
    if (value < -273.15) {
      throw new Error('Temperature below absolute zero');
    }
    this._celsius = value;
  }
  
  get fahrenheit() {
    return (this._celsius * 9/5) + 32;
  }
  
  set fahrenheit(value) {
    this.celsius = (value - 32) * 5/9;
  }
}

class Weather extends Temperature {
  constructor(celsius, condition) {
    super(celsius);
    this.condition = condition;
  }
  
  get description() {
    return `Weather: ${this.condition}, Temperature: ${super.fahrenheit}°F`;
  }
  
  set description(value) {
    // 通过 setter 设置父类属性
    super.fahrenheit = parseFloat(value.match(/\d+/)[0]);
  }
}

const weather = new Weather(25, 'Sunny');
console.log(weather.description); // Weather: Sunny, Temperature: 77°F

weather.description = 'Temperature: 86°F';
console.log(weather.celsius); // 30

内置对象的继承

继承内置对象

javascript
class ExtendedArray extends Array {
  constructor(...args) {
    super(...args);
  }
  
  first() {
    return this[0];
  }
  
  last() {
    return this[this.length - 1];
  }
  
  sum() {
    return this.reduce((acc, val) => acc + val, 0);
  }
}

const arr = new ExtendedArray(1, 2, 3, 4, 5);
console.log(arr.first()); // 1
console.log(arr.last()); // 5
console.log(arr.sum()); // 15
console.log(arr instanceof Array); // true
console.log(arr instanceof ExtendedArray); // true

// 内置方法返回正确类型的实例
const filtered = arr.filter(x => x > 2);
console.log(filtered instanceof ExtendedArray); // true

继承 Error 对象

javascript
class CustomError extends Error {
  constructor(message, code) {
    super(message);
    this.name = this.constructor.name;
    this.code = code;
  }
}

class ValidationError extends CustomError {
  constructor(message, field) {
    super(message, 'VALIDATION_ERROR');
    this.field = field;
  }
}

try {
  throw new ValidationError('Invalid email', 'email');
} catch (error) {
  console.log(error.name); // ValidationError
  console.log(error.message); // Invalid email
  console.log(error.code); // VALIDATION_ERROR
  console.log(error.field); // email
  console.log(error instanceof Error); // true
  console.log(error instanceof CustomError); // true
  console.log(error instanceof ValidationError); // true
}

Class 表达式和 Mixins

Class 表达式

javascript
// Class 表达式
const MyClass = class {
  constructor(name) {
    this.name = name;
  }
  
  greet() {
    return `Hello, ${this.name}`;
  }
};

const instance = new MyClass('Alice');
console.log(instance.greet()); // Hello, Alice

// 带名称的 Class 表达式
const AnotherClass = class NamedClass {
  constructor(value) {
    this.value = value;
  }
  
  getValue() {
    return this.value;
  }
};

const anotherInstance = new AnotherClass(42);
console.log(anotherInstance.getValue()); // 42

Mixins 模式

javascript
// Mixin 函数
const TimestampMixin = (Superclass) => class extends Superclass {
  constructor(...args) {
    super(...args);
    this.createdAt = new Date();
  }
  
  getAge() {
    return Date.now() - this.createdAt.getTime();
  }
};

const SerializableMixin = (Superclass) => class extends Superclass {
  serialize() {
    return JSON.stringify(this);
  }
  
  static deserialize(str) {
    return new this(JSON.parse(str));
  }
};

// 使用 Mixins
class User {
  constructor(name) {
    this.name = name;
  }
}

class TimestampedUser extends TimestampMixin(User) {}
class SerializableUser extends SerializableMixin(User) {}
class CompleteUser extends TimestampMixin(SerializableMixin(User)) {}

const user = new CompleteUser('Alice');
console.log(user.name); // Alice
console.log(user.createdAt); // 创建时间
console.log(user.serialize()); // 序列化后的用户对象

最佳实践

1. 正确使用 super()

javascript
// 好的做法:在构造函数中首先调用 super()
class Parent {
  constructor(name) {
    this.name = name;
  }
}

class Child extends Parent {
  constructor(name, age) {
    super(name); // 首先调用
    this.age = age; // 然后使用 this
  }
}

// 避免在 super() 之前使用 this
class BadChild extends Parent {
  constructor(name, age) {
    // this.age = age; // ReferenceError: Must call super constructor
    super(name);
    this.age = age;
  }
}

2. 理解原型链

javascript
// 理解原型链对于调试很重要
class A {
  methodA() { return 'A'; }
}

class B extends A {
  methodB() { return 'B'; }
}

class C extends B {
  methodC() { return 'C'; }
}

const c = new C();

// 检查原型链
console.log(c.__proto__ === C.prototype); // true
console.log(C.prototype.__proto__ === B.prototype); // true
console.log(B.prototype.__proto__ === A.prototype); // true
console.log(A.prototype.__proto__ === Object.prototype); // true

3. 合理使用静态方法

javascript
class MathUtils {
  static add(a, b) {
    return a + b;
  }
  
  static multiply(a, b) {
    return a * b;
  }
  
  // 工厂方法
  static createZero() {
    return new this(0);
  }
  
  constructor(value) {
    this.value = value;
  }
}

const zero = MathUtils.createZero();
console.log(zero.value); // 0
console.log(MathUtils.add(2, 3)); // 5

总结

ES6 的 class 语法虽然看起来像传统的面向对象语言中的类,但它本质上仍然是 JavaScript 原型继承机制的语法糖。通过 extends 关键字实现的继承,背后是通过设置原型链来实现的。理解 class 语法背后的原型操作机制,有助于我们更好地使用 JavaScript 的面向对象特性,避免常见的陷阱,并编写出更加健壮和可维护的代码。super 关键字的正确使用、静态方法的继承以及与内置对象的继承都是需要深入理解的重要概念。