一、Class 基本语法
1. 类声明与类表达式
ES6 引入了更接近传统语言的类声明方式,但本质上仍然是基于原型的语法糖。
// 类声明
class Person {
constructor(name) {
this.name = name;
}
sayHello() {
return `Hello, ${this.name}`;
}
}
// 类表达式
const Animal = class {
constructor(type) {
this.type = type;
}
};
2. 构造方法
constructor 方法是类的默认方法,通过 new 命令生成对象实例时自动调用。
class Point {
constructor(x, y) {
this.x = x; // 实例属性
this.y = y;
}
toString() {
return `(${this.x}, ${this.y})`;
}
}
const p = new Point(1, 2);
console.log(p.toString()); // (1, 2)
二、Class 的核心特性
1. 方法定义
类的方法都定义在类的 prototype 属性上,实例调用方法实际上就是调用原型上的方法。
class Person {
constructor() {}
speak() {}
eat() {}
}
// 等同于
Person.prototype = {
constructor() {},
speak() {},
eat() {}
};
2. 静态方法与属性
静态方法/属性使用 static 关键字修饰,不会被实例继承,而是直接通过类调用。
class MyClass {
static staticMethod() {
return 'static method';
}
static staticProperty = 'static property';
}
console.log(MyClass.staticMethod()); // "static method"
console.log(MyClass.staticProperty); // "static property"
3. 私有字段与方法
ES2022 正式为 class 添加了私有属性和方法,在属性名之前使用 # 表示。
class Counter {
#count = 0; // 私有字段
#increment() { // 私有方法
this.#count++;
}
tick() {
this.#increment();
console.log(this.#count);
}
}
const c = new Counter();
c.tick(); // 1
// c.#count; // SyntaxError
三、Class 的继承机制
1. extends 继承
使用 extends 关键字实现继承,比 ES5 的原型链继承更清晰。
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // 调用父类构造函数
this.breed = breed;
}
speak() {
super.speak(); // 调用父类方法
console.log(`${this.name} barks.`);
}
}
const d = new Dog('Rex', 'Labrador');
d.speak();
// Rex makes a noise.
// Rex barks.
2. super 关键字
super 有两种使用方式:
- 作为函数调用(
super()),代表父类构造函数 - 作为对象调用(
super.method()),指向父类原型对象
class Parent {
constructor() {
this.name = 'Parent';
}
say() {
return this.name;
}
}
class Child extends Parent {
constructor() {
super(); // 必须在使用this前调用
this.childName = 'Child';
}
say() {
return super.say() + ' & ' + this.childName;
}
}
四、Class 的底层实现
1. 与 ES5 原型继承的对应关系
// ES6
class Person {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}
// 等同于 ES5
function Person(name) {
this.name = name;
}
Person.prototype.sayName = function() {
console.log(this.name);
};
2. 继承的底层实现
// ES6
class Animal {}
class Dog extends Animal {}
// 等同于 ES5
function Animal() {}
function Dog() {}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Object.setPrototypeOf(Dog, Animal);
五、Class 的注意事项
1. 不存在变量提升
new Foo(); // ReferenceError
class Foo {}
2. this 指向问题
类方法内部的 this 默认指向实例,但单独提取方法可能改变 this 指向。
class Logger {
printName(name = 'default') {
this.print(`Hello ${name}`);
}
print(text) {
console.log(text);
}
}
const logger = new Logger();
const { printName } = logger;
printName(); // TypeError: Cannot read property 'print' of undefined
解决方案:
// 1. 在构造函数中绑定this
class Logger {
constructor() {
this.printName = this.printName.bind(this);
}
}
// 2. 使用箭头函数
class Logger {
printName = (name = 'default') => {
this.print(`Hello ${name}`);
}
}
// 3. 使用Proxy自动绑定
六、Class 的高级特性
1. 存取器(getter/setter)
class User {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
set fullName(value) {
[this.firstName, this.lastName] = value.split(' ');
}
}
const user = new User('John', 'Doe');
console.log(user.fullName); // "John Doe"
user.fullName = 'Jane Smith';
console.log(user.firstName); // "Jane"
2. 类字段声明语法
ES2022 新增的类字段提案:
class Counter {
count = 0; // 实例字段
increment = () => { // 箭头函数方法
this.count++;
};
static version = '1.0'; // 静态字段
}
3. 静态初始化块
ES2022 引入静态初始化块,用于类静态端的复杂初始化。
class Translator {
static translations = {
yes: 'ja',
no: 'nein',
maybe: 'vielleicht',
};
static englishWords = [];
static germanWords = [];
static { // 静态初始化块
for (const [english, german] of Object.entries(this.translations)) {
this.englishWords.push(english);
this.germanWords.push(german);
}
}
}
七、Class 的应用场景
1. 框架中的组件设计
React 类组件:
class MyComponent extends React.Component {
state = { count: 0 };
handleClick = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<button onClick={this.handleClick}>
Clicked {this.state.count} times
</button>
);
}
}
2. 复杂业务模型
class ShoppingCart {
#items = [];
addItem(item) {
this.#items.push(item);
}
removeItem(id) {
this.#items = this.#items.filter(item => item.id !== id);
}
get total() {
return this.#items.reduce((sum, item) => sum + item.price, 0);
}
static createFromJSON(json) {
const cart = new ShoppingCart();
JSON.parse(json).forEach(item => cart.addItem(item));
return cart;
}
}
八、Class 与原型的关系总结
- 语法糖本质:class 本质上是构造函数的语法糖
- 继承实现:extends 实现了基于原型的继承
- 方法存储:类方法存储在原型对象上
- 静态成员:静态方法/属性存储在构造函数上
- 实例属性:constructor 内定义的属性是实例自有属性
class Example {
instanceProperty = 'instance';
static staticProperty = 'static';
instanceMethod() {}
static staticMethod() {}
}
// 等价关系
console.log(typeof Example); // "function" (构造函数)
console.log(Example.prototype.instanceMethod); // function
console.log(Example.staticMethod); // function
console.log(Example.prototype.constructor === Example); // true