ES6+ 类
class 是 ES6 引入的语法糖,底层仍然是原型链机制,但提供了更清晰、更接近传统面向对象语言的书写方式。本文系统讲解类的全部语法特性,并与构造函数方式做对比。
一、class 语法
1.1 类声明
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
console.log(`你好,我是${this.name}`);
}
}
const p = new Person("张三", 25);
p.greet(); // 你好,我是张三
console.log(typeof Person); // function,类的本质是函数1.2 类表达式
类可以像函数一样作为表达式赋值给变量,甚至即时使用:
const Person = class {
constructor(name) {
this.name = name;
}
};
// 具名类表达式,名字只在类内部可见
const Animal = class Dog {
getType() {
return Dog.name; // 类内部可用 Dog
}
};
console.log(new Animal().getType()); // Dog注意类声明不会提升,与函数声明不同,必须先声明后使用:
const p = new Person(); // ReferenceError
class Person {}二、constructor 构造函数
constructor 在 new 时自动调用,负责初始化实例;一个类只能有一个 constructor:
class Person {
constructor(name) {
this.name = name;
}
}
const p = new Person("张三");
console.log(p.name); // 张三
// 不写 constructor 时默认有一个空构造:constructor() {}
class Empty {}
console.log(new Empty()); // Empty {}constructor 中 return 对象会覆盖实例,返回原始值则被忽略(与构造函数行为一致)。
三、实例属性与类字段(class fields)
ES2022 起支持在类体内直接声明字段,无需经过 constructor:
class Counter {
// 类字段:每个实例独立拥有
count = 0;
label = "计数"; // 字段可以有初始值
constructor(start) {
if (start !== undefined) this.count = start;
}
increment() {
this.count++;
}
}
const c1 = new Counter();
const c2 = new Counter(10);
c1.increment();
console.log(c1.count, c2.count); // 1 10,互不影响字段定义在 constructor 之前执行,因此可以在 constructor 中读取:
class Demo {
value = 100;
constructor() {
console.log(this.value); // 100,字段已初始化
}
}
new Demo();四、实例方法
类体内的普通方法定义在 prototype 上,被所有实例共享:
class Calculator {
add(a, b) {
return a + b;
}
}
const c1 = new Calculator();
const c2 = new Calculator();
console.log(c1.add === c2.add); // true,共享同一份
console.log(Calculator.prototype.add === c1.add); // true箭头函数字段则属于实例自身,适合作为回调(this 固定为实例):
class Button {
clicks = 0;
// 类字段使用箭头函数:this 永远指向实例
onClick = () => {
this.clicks++;
console.log(this.clicks);
};
}
const btn = new Button();
const fn = btn.onClick; // 取出后调用,this 仍是实例
fn(); // 1五、静态方法与静态属性(static)
static 成员挂在类本身而非原型上,通过类名调用:
class MathUtils {
static PI = 3.14159; // 静态字段
static square(x) { // 静态方法
return x * x;
}
static createDefault() { // 静态工厂方法
return new MathUtils();
}
}
console.log(MathUtils.PI); // 3.14159
console.log(MathUtils.square(4)); // 16
console.log(MathUtils.prototype.square); // undefined,不在原型上静态方法中的 this 指向类本身,可用来调用其他静态成员:
class User {
static maxAge = 150;
static isValidAge(age) {
return age > 0 && age <= this.maxAge;
}
}
console.log(User.isValidAge(30)); // true
console.log(User.isValidAge(200)); // false六、getter / setter
通过 get / set 关键字定义访问器属性,读写时执行逻辑:
class Person {
#age; // 私有字段,见下一节
constructor(name, age) {
this.name = name;
this.#age = age;
}
get age() {
return this.#age;
}
set age(value) {
if (value < 0 || value > 150) {
throw new Error("年龄不合法");
}
this.#age = value;
}
}
const p = new Person("张三", 25);
console.log(p.age); // 25,触发 getter
p.age = 30; // 触发 setter
// p.age = 200; // 抛错:年龄不合法七、私有字段与方法(# 语法,ES2022)
以 # 开头的字段和方法是真私有的,类外部无法访问(语法层面报错,非约定):
class BankAccount {
#balance = 0; // 私有字段
#secretLog() { // 私有方法
return `余额:${this.#balance}`;
}
deposit(amount) {
this.#balance += amount;
}
getStatus() {
return this.#secretLog(); // 类内部可正常访问
}
}
const acc = new BankAccount();
acc.deposit(100);
console.log(acc.getStatus()); // 余额:100
console.log(acc.#balance); // SyntaxError,类外部不可访问| 成员 | 访问范围 | 示例 |
|---|---|---|
| 公共字段 | 类内外均可 | count = 0 |
| 私有字段 | 仅类内部 | #count = 0 |
| 静态私有 | 仅类内部,通过类名访问 | static #total = 0 |
| 私有方法 | 仅类内部 | #helper() {} |
class Order {
static #orders = 0; // 静态私有字段
constructor() {
Order.#orders++; // 类内部通过类名访问
}
static getCount() {
return Order.#orders;
}
}
new Order();
new Order();
console.log(Order.getCount()); // 2八、类继承(extends / super)
8.1 extends 与 super 调用父类构造函数
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name}发出声音`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // 必须先调用 super 再使用 this
this.breed = breed;
}
speak() {
super.speak(); // 调用父类方法
console.log("汪汪");
}
}
const dog = new Dog("旺财", "柴犬");
dog.speak();
// 旺财发出声音
// 汪汪规则:子类 constructor 中必须先调用 super() 才能使用 this;不写 constructor 时默认执行 constructor(...args) { super(...args); }。
8.2 方法重写
子类定义同名方法即覆盖父类方法,仍可用 super 访问被覆盖的版本:
class Shape {
area() {
return 0;
}
describe() {
return `面积:${this.area()}`;
}
}
class Circle extends Shape {
constructor(radius) {
super();
this.radius = radius;
}
area() { // 重写父类方法
return Math.PI * this.radius ** 2;
}
}
const c = new Circle(1);
console.log(c.describe()); // 面积:3.141592653589793
console.log(c.area()); // 3.1415926535897938.3 super 关键字总结
| 位置 | 含义 |
|---|---|
super() | 子类构造中调用父类构造函数 |
super.method() | 调用父类原型上的方法 |
super.field | 读取父类原型上的属性(类字段在实例上,需 super() 后经 this 读取) |
九、new.target
new.target 在函数体内指向"当前执行 new 的构造函数",用于检测调用方式:
function check() {
console.log(new.target);
}
check(); // undefined,普通调用
new check(); // function check() {...},new 调用
// 在类中判断实例的来源
class Animal {
constructor() {
console.log(new.target === Animal); // 直接 new Animal 为 true
}
}
class Dog extends Animal {}
new Animal(); // true
new Dog(); // false,new.target 是 Dog利用 new.target 可以禁止直接实例化类,模拟抽象类:
class Shape {
constructor() {
if (new.target === Shape) {
throw new Error("Shape 是抽象类,不能直接实例化");
}
}
area() { // 抽象方法:子类必须重写
throw new Error("子类必须实现 area 方法");
}
}
class Circle extends Shape {
constructor(r) {
super();
this.r = r;
}
area() {
return Math.PI * this.r ** 2;
}
}
// new Shape(); // 抛错:抽象类不能实例化
console.log(new Circle(1).area()); // 3.141592653589793十、类的本质
class 是构造函数的语法糖,其底层仍是原型链:
class Person {
constructor(name) {
this.name = name;
}
greet() {}
static info() {}
}
// 对照验证
console.log(typeof Person); // function
console.log(Person.prototype.greet); // 实例方法在 prototype 上
console.log(Person.info); // 静态方法在类上
console.log(Person.prototype.constructor === Person); // true
// 与构造函数的等价关系:下面两种写法结构完全一致
function PersonOld(name) {
this.name = name;
}
PersonOld.prototype.greet = function () {};
console.log(typeof Person); // function
console.log(Person.prototype.greet); // 实例方法在 prototype 上
console.log(PersonOld.prototype.greet); // 同样在 prototype 上与普通函数的差异:
| 特性 | 普通函数 | class |
|---|---|---|
| 声明提升 | 是 | 否 |
是否可用 new | 均可 | 必须 new |
不用 new 调用 | 可(Person()) | 抛错(class 只能 new) |
是否可迭代属性(enumerable) | 方法可枚举 | 方法默认不可枚举 |
| 严格模式 | 视情况 | 方法体默认严格模式 |
十一、静态块(static block,ES2022)
静态块在类定义时执行一次,用于完成静态字段的复杂初始化:
class Config {
static settings = {};
static {
// 类加载时执行一次,可读取外部环境
Config.settings.mode = process.env.NODE_ENV || "development";
Config.settings.version = "1.0.0";
}
}
console.log(Config.settings);
// { mode: 'development', version: '1.0.0' }// 静态块可以访问私有静态字段,做跨实例统计
class Counter {
static #total = 0;
static {
Counter.#total = 100; // 初始化私有静态字段
}
static get total() {
return Counter.#total;
}
}
console.log(Counter.total); // 100十二、继承链与原型链关系
class 继承同时建立了两条链:实例链(属性与方法查找)与类链(静态成员查找):
class Parent {}
class Child extends Parent {}
const c = new Child();
// 实例链:c → Child.prototype → Parent.prototype → Object.prototype
console.log(c instanceof Child); // true
console.log(c instanceof Parent); // true
console.log(Child.prototype.__proto__ === Parent.prototype); // true
// 类链:Child → Parent(extends 使子类继承父类静态成员)
console.log(Child.__proto__ === Parent); // true
console.log(Parent.__proto__ === Function.prototype); // true因此子类可以访问父类的静态成员:
class Parent {
static brand = "P";
}
class Child extends Parent {}
console.log(Child.brand); // P,静态成员沿类链继承十三、多态
不同子类以相同接口响应,运行时根据实际类型调用对应实现:
class Animal {
speak() {
console.log("动物叫");
}
}
class Dog extends Animal {
speak() {
console.log("汪汪");
}
}
class Cat extends Animal {
speak() {
console.log("喵喵");
}
}
const animals = [new Animal(), new Dog(), new Cat()];
animals.forEach((a) => a.speak());
// 动物叫
// 汪汪
// 喵喵十四、mixins 组合模式
单继承之外,可用函数返回的类实现多类能力组合:
// mixin:接收一个类,返回扩展后的新类
const Flyable = (Base) =>
class extends Base {
fly() {
console.log(`${this.name}飞起来了`);
}
};
const Swimmable = (Base) =>
class extends Base {
swim() {
console.log(`${this.name}在游泳`);
}
};
class Animal {
constructor(name) {
this.name = name;
}
}
// 组合多个 mixin
class Duck extends Swimmable(Flyable(Animal)) {}
const duck = new Duck("唐老鸭");
duck.fly(); // 唐老鸭飞起来了
duck.swim(); // 唐老鸭在游泳class 语法让面向对象编码更直观,但请记住它始终建立在原型链之上——理解《原型与原型链》中的查找与继承机制,才能真正驾驭类的高级特性。