讲解
class 是 ES6 引入的面向对象语法,把构造函数 + 原型的经典模式包装成更熟悉的形式:constructor 方法在 new 时执行初始化,普通方法自动挂到原型上被所有实例共享,get/set 定义访问器属性,static 定义类本身的静态成员(不随实例走,用 类名.成员 访问),extends 继承父类、super 调用父类构造与方法。类声明不会提升,先声明后使用。
ES2022 补全了类的现代特性:公有字段直接写在类体里(count = 0 等价于在构造函数里 this.count = 0);# 前缀的私有字段和私有方法(#secret)在类外部完全不可访问,是真正的私有(区别于约定俗成的 _ 下划线);static 块可以做复杂的静态初始化。这些特性让 class 成为组织中大型代码的主力方式。
继承的使用要克制:优先组合(把功能对象作为字段持有)而不是深层继承链,父类改动波及所有子类是维护噩梦。class 与原型并不矛盾——class 就是原型机制的外衣,前面一章的原型知识在这里全部适用:方法在 prototype 上共享、实例沿原型链找方法、instanceof 检查原型链。
示例
class Counter {
#count = 0; // 私有字段,外部不可访问
static totalCreated = 0; // 静态字段
constructor(start = 0) {
this.#count = start;
Counter.totalCreated++;
}
increment(step = 1) {
this.#count += step;
return this.#count;
}
get value() {
return this.#count;
}
static create() {
return new Counter(0);
}
}
const c = Counter.create();
c.increment();
c.increment(5);
console.log('当前值:', c.value);
console.log('创建过几个实例:', Counter.totalCreated);
console.log('私有字段外部读不到:', c['#count']); // undefined
// 继承
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return this.name + ' 叫了一声';
}
}
class Dog extends Animal {
speak() {
return super.speak() + '(汪汪)';
}
}
const dog = new Dog('旺财');
console.log(dog.speak());
console.log('instanceof:', dog instanceof Dog, dog instanceof Animal);
常见坑
- 忘记 new:直接 Counter() 调用类会抛 TypeError,class 不能像普通函数一样调用。
- 类方法作为回调丢失 this:onClick(this.save) 传出去后 this 丢失,因为类方法本质还是原型上的普通函数。用 bind 或箭头函数类字段(save = () => {...})。
- 在 constructor 之前使用 this:子类构造函数里必须先调用 super() 才能碰 this,否则抛 ReferenceError。
- 以为 # 私有是下划线约定:_count 只是命名约定,外部照样能改;#count 才是语言强制的私有,但注意它不能被 this['#count'] 动态访问。
小结
class 封装构造、方法、静态成员与继承,# 字段实现真私有;方法共享依旧靠原型。继承要克制,组合优先。下一节学习让取值更优雅的解构赋值。