ES6 的 Class 是基于原型继承的语法糖,让面向对象编程更加直观清晰。
基本语法#
定义类#
class Person { // 构造函数 constructor(name, age) { this.name = name this.age = age }
// 实例方法 sayHello() { console.log(`你好,我是 ${this.name}`) }
// getter get info() { return `${this.name}, ${this.age}岁` }
// setter set info(value) { ;[this.name, this.age] = value.split(', ') }}
const person = new Person('张三', 25)person.sayHello() // 你好,我是 张三console.log(person.info) // 张三, 25岁ES5 对比#
// ES6 Classclass Person { constructor(name) { this.name = name }
sayHello() { console.log(`Hello, ${this.name}`) }}
// 等价的 ES5 写法function Person(name) { this.name = name}
Person.prototype.sayHello = function () { console.log('Hello, ' + this.name)}类的本质#
class Person {}
typeof Person // 'function'Person === Person.prototype.constructor // true
// 类的方法定义在原型上class Point { constructor(x, y) { this.x = x this.y = y }
toString() { return `(${this.x}, ${this.y})` }}
Point.prototype.toString // [Function: toString]constructor 方法#
基本规则#
class Person { constructor(name) { this.name = name // 默认返回 this }}
// 不写 constructor 会有默认的空构造函数class Empty { // constructor() {}}返回值#
// 返回其他对象会改变 new 的结果class Foo { constructor() { return { custom: true } }}
const foo = new Foo()foo instanceof Foo // falsefoo.custom // true必须使用 new#
class Person { constructor(name) { this.name = name }}
// Person('张三'); // TypeError: Class constructor cannot be invoked without 'new'new Person('张三') // OK实例属性#
在 constructor 中定义#
class Person { constructor(name) { this.name = name this.friends = [] }}类字段声明(ES2022)#
class Person { // 公有字段 name = '默认名称' age = 0 friends = []
constructor(name, age) { if (name) this.name = name if (age) this.age = age }}
const person = new Person()console.log(person.name) // '默认名称'私有字段(ES2022)#
class BankAccount { #balance = 0 // 私有字段
constructor(initial) { this.#balance = initial }
deposit(amount) { this.#balance += amount }
withdraw(amount) { if (amount > this.#balance) { throw new Error('余额不足') } this.#balance -= amount }
get balance() { return this.#balance }}
const account = new BankAccount(1000)account.deposit(500)console.log(account.balance) // 1500// console.log(account.#balance); // SyntaxError: 私有字段实例方法#
方法简写#
class Calculator { add(a, b) { return a + b }
subtract(a, b) { return a - b }}
// 方法在原型上,所有实例共享const calc1 = new Calculator()const calc2 = new Calculator()calc1.add === calc2.add // true私有方法(ES2022)#
class Person { #privateMethod() { return '私有方法' }
publicMethod() { return this.#privateMethod() }}
const person = new Person()person.publicMethod() // '私有方法'// person.#privateMethod(); // SyntaxError生成器方法#
class Range { constructor(start, end) { this.start = start this.end = end }
*[Symbol.iterator]() { for (let i = this.start; i <= this.end; i++) { yield i } }}
;[...new Range(1, 5)] // [1, 2, 3, 4, 5]getter 和 setter#
基本用法#
class Circle { constructor(radius) { this._radius = radius }
get radius() { return this._radius }
set radius(value) { if (value < 0) { throw new Error('半径不能为负数') } this._radius = value }
get area() { return Math.PI * this._radius ** 2 }
get circumference() { return 2 * Math.PI * this._radius }}
const circle = new Circle(5)console.log(circle.area) // 78.54...circle.radius = 10console.log(circle.area) // 314.16...计算属性#
class Rectangle { constructor(width, height) { this.width = width this.height = height }
get area() { return this.width * this.height }
get perimeter() { return 2 * (this.width + this.height) }
get diagonal() { return Math.sqrt(this.width ** 2 + this.height ** 2) }}静态成员#
静态方法#
class MathUtils { static add(a, b) { return a + b }
static multiply(a, b) { return a * b }
static PI = 3.14159}
MathUtils.add(1, 2) // 3MathUtils.PI // 3.14159
// 静态方法不能通过实例调用const utils = new MathUtils()// utils.add(1, 2); // TypeError静态字段(ES2022)#
class Config { static version = '1.0.0' static #secret = 'private' // 私有静态字段
static getSecret() { return this.#secret }}
Config.version // '1.0.0'Config.getSecret() // 'private'工厂模式#
class User { constructor(name, role) { this.name = name this.role = role }
static createAdmin(name) { return new User(name, 'admin') }
static createGuest() { return new User('访客', 'guest') }}
const admin = User.createAdmin('张三')const guest = User.createGuest()单例模式#
class Database { static #instance = null
constructor() { if (Database.#instance) { return Database.#instance } Database.#instance = this }
static getInstance() { if (!Database.#instance) { Database.#instance = new Database() } return Database.#instance }
query(sql) { console.log('执行查询:', sql) }}
const db1 = Database.getInstance()const db2 = Database.getInstance()db1 === db2 // true静态初始化块(ES2022)#
class Config { static settings static #privateData
static { // 复杂的静态初始化逻辑 try { const data = loadConfig() this.settings = data.settings this.#privateData = data.secret } catch (error) { this.settings = getDefaultSettings() this.#privateData = null } }
static getPrivateData() { return this.#privateData }}类表达式#
匿名类表达式#
const Person = class { constructor(name) { this.name = name }
sayHello() { console.log(`Hello, ${this.name}`) }}
new Person('张三').sayHello()命名类表达式#
const Person = class PersonClass { constructor(name) { this.name = name }
static create(name) { return new PersonClass(name) // 内部可以使用 }}
// PersonClass 只在类内部可用// new PersonClass('张三'); // ReferenceError立即执行的类#
const person = new (class { constructor(name) { this.name = name }
sayHello() { return `Hello, ${this.name}` }})('张三')
person.sayHello() // 'Hello, 张三'this 指向#
方法中的 this#
class Counter { count = 0
increment() { this.count++ }}
const counter = new Counter()
// 直接调用没问题counter.increment()console.log(counter.count) // 1
// 但作为回调时 this 会丢失const { increment } = counter// increment(); // TypeError: Cannot read property 'count' of undefined解决方案#
// 方案1:箭头函数字段class Counter { count = 0
increment = () => { this.count++ }}
// 方案2:bindclass Counter { constructor() { this.increment = this.increment.bind(this) }
count = 0
increment() { this.count++ }}
// 方案3:调用时绑定const counter = new Counter()button.addEventListener('click', () => counter.increment())in 操作符检查私有字段#
class Person { #name
constructor(name) { this.#name = name }
static isPerson(obj) { return #name in obj }}
Person.isPerson(new Person('张三')) // truePerson.isPerson({}) // false常见模式#
链式调用#
class QueryBuilder { #query = ''
select(fields) { this.#query += `SELECT ${fields} ` return this }
from(table) { this.#query += `FROM ${table} ` return this }
where(condition) { this.#query += `WHERE ${condition} ` return this }
build() { return this.#query.trim() }}
const query = new QueryBuilder() .select('*') .from('users') .where('age > 18') .build()
// 'SELECT * FROM users WHERE age > 18'混入(Mixin)#
const Serializable = (Base) => class extends Base { toJSON() { return JSON.stringify({ ...this }) }
static fromJSON(json) { return Object.assign(new this(), JSON.parse(json)) } }
const Comparable = (Base) => class extends Base { equals(other) { return JSON.stringify(this) === JSON.stringify(other) } }
class User extends Serializable(Comparable(Object)) { constructor(name, age) { super() this.name = name this.age = age }}
const user = new User('张三', 25)console.log(user.toJSON()) // '{"name":"张三","age":25}'小结#
| 特性 | 说明 |
|---|---|
| class | 类声明 |
| constructor | 构造函数 |
| 实例方法 | 定义在原型上的方法 |
| 静态成员 | static 关键字定义 |
| 私有成员 | # 前缀(ES2022) |
| getter/setter | 访问器属性 |
Class 让 JavaScript 的面向对象编程更加清晰,但本质上仍是基于原型的继承。