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); // trueES6 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); // trueClass 与原型链的关系
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); // trueClass 的内部属性
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')); // trueextends 继承的实现机制
基本继承
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); // trueextends 背后的原型链操作
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()); // Animalsuper 关键字的实现机制
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() callsuper 在方法中的使用
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); // truegetter 和 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()); // 42Mixins 模式
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); // true3. 合理使用静态方法
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 关键字的正确使用、静态方法的继承以及与内置对象的继承都是需要深入理解的重要概念。