ES6 的 class 语法不仅提供了更贴近传统面向对象语言的类定义方式,还带来了清晰的继承机制和成员访问控制能力。下面分别讲解继承的实现、super 关键字的作用,以及如何定义和使用私有属性。
继承:使用 extends 构建类层级
类的继承通过 extends 关键字实现,底层依然是基于原型链的委托,只是语法更简洁、语义更明确。
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} 发出声音`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // 调用父类构造函数
this.breed = breed;
}
speak() {
console.log(`${this.name} 汪汪叫`);
}
}
const d = new Dog('旺财', '金毛');
d.speak(); // 旺财 汪汪叫
extends建立了两个原型链关系:Dog.prototype.proto === Animal.prototype(实例方法继承),以及Dog.proto === Animal(静态方法继承)。- 子类可以覆盖(重写)父类的方法,实现多态;也可以通过
super调用父类版本(见下文)。 - 如果不显式定义
constructor,引擎会自动生成一个默认构造函数,并调用super(...args)将参数传递给父类。
super 关键字:连接父类的桥梁
super 在继承体系中有两个主要用途:
- 在子类构造函数中调用父类构造函数
super(...) 必须在使用 this 之前调用,否则会报错。这是因为 ES6 的类先创建父类实例,再由子类构造函数去修改它。
- 在子类方法中访问父类方法
无论是普通方法还是静态方法,都可以用 super.methodName() 调用父类的对应方法。
class Cat extends Animal {
speak() {
super.speak(); // 调用父类的 speak 方法
console.log('喵喵~');
}
}
const c = new Cat('小花');
c.speak();
// 小花 发出声音
// 喵喵~
super在静态方法中同样可用,指向父类的静态方法。- 注意:
super不能像一个独立变量一样被单独引用或赋值,它只能在方法内部使用。
私有属性:安全的内部状态
ES2022 正式引入了私有属性和私有方法,使用 # 前缀定义。这些成员只能被类内部访问,外部直接访问或继承的子类都无法获取。
class Counter {
#count = 0; // 私有实例属性
#reset() { // 私有方法
this.#count = 0;
}
increment() {
this.#count++;
}
get value() {
return this.#count;
}
doReset() {
this.#reset(); // 内部可调用私有方法
}
}
const c = new Counter();
c.increment();
console.log(c.value); // 1
console.log(c.#count); // SyntaxError(语法错误)
关键规则:
- 私有属性必须在类顶层显式声明(例如
#count = 0),不能在构造函数中动态添加。 - 子类完全无法访问父类的私有属性,即使使用
super也不行。 - 私有方法(如
#reset)同样只能在类内部调用。
私有属性补上了 JavaScript 类长久以来缺失的“封装”能力,让开发者能够保护内部状态不被意外修改,也不再依赖于 _ 前缀约定或 WeakMap 等 hack 方式。
小结
extends让继承关系一目了然,原型链自动连通。super是调用父类方法和构造函数的标准方式,必须在访问this前使用。#私有成员提供了真正的封装,外部代码无法闯入,适用于需要严格保护的状态。
掌握了这三者,你已经能够用 class 写出结构清晰、职责明确、封装良好的面向对象代码。