JavaScript ES6 Class详解
·
文章目录
-
-
- 1. Class 基本语法
- 2. Class 与构造函数的对比
- 3. Class 的重要特性
- 4. 继承机制
- 5. 静态方法和属性
- 6. Getter 和 Setter
- 7. 私有属性和方法(ES2022)
- 8. 实例属性定义
- 9. Mixins 模式
- 10. Class 表达式
- 11. Class 的 this 绑定
- 12. Class 与原型链
- 13. 实际应用示例
- 使用注意事项
-
- 1. **`class` 不是真正的“类” —— 它是语法糖**
- 2. **必须使用 `new` 调用,否则报错**
- 3. **不存在变量提升(No Hoisting)**
- 4. **类内部默认严格模式**
- 5. **`this` 指向问题 —— 方法解构后丢失上下文**
- 解决方案:
- 6. **私有字段必须显式声明**
- 7. **静态方法不能访问实例属性/方法**
- 8. **继承时必须调用 `super()`**
- 9. **`super` 的使用限制**
- 10. **`class` 不能枚举,但方法可以被继承**
- 11. **getter/setter 是属性,不是方法**
- 12. **不要在 getter 中有副作用**
- 最佳实践总结
- 总结
-
1. Class 基本语法
基础定义
// ES6 Class 语法
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
// 实例方法
sayHello() {
console.log(`Hello, I'm ${this.name}`);
}
// 静态方法
static getSpecies() {
return 'Homo sapiens';
}
// Getter
get info() {
return `${this.name}, ${this.age} years old`;
}
// Setter
set nickname(value) {
this._nickname = value;
}
}
// 使用
const person = new Person('Alice', 25);
person.sayHello(); // "Hello, I'm Alice"
console.log(Person.getSpecies()); // "Homo sapiens"
console.log(person.info); // "Alice, 25 years old"
2. Class 与构造函数的对比
构造函数方式(ES5)
function PersonES5(name, age) {
this.name = name;
this.age = age;
}
PersonES5.prototype.sayHello = function() {
console.log(`Hello, I'm ${this.name}`);
};
PersonES5.getSpecies = function() {
return 'Homo sapiens';
};
Class 方式(ES6)
class PersonES6 {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`Hello, I'm ${this.name}`);
}
static getSpecies() {
return 'Homo sapiens';
}
}
3. Class 的重要特性
严格模式
class MyClass {
// Class 内部默认启用严格模式
method() {
// this 在严格模式下未绑定时为 undefined
}
}
必须使用 new 调用
class MyClass {}
// const obj = MyClass(); // TypeError: Class constructor MyClass cannot be invoked without 'new'
const obj = new MyClass(); // 正确
不可枚举的方法
class Test {
method() {}
}
console.log(Object.keys(Test.prototype)); // []
console.log(Object.getOwnPropertyNames(Test.prototype)); // ['constructor', 'method']
4. 继承机制
基本继承
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a sound`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // 调用父类构造函数
this.breed = breed;
}
speak() {
super.speak(); // 调用父类方法
console.log(`${this.name} barks`);
}
getInfo() {
return `${this.name} is a ${this.breed}`;
}
}
const dog = new Dog('Buddy', 'Golden Retriever');
dog.speak();
// "Buddy makes a sound"
// "Buddy barks"
继承内置对象
class MyArray extends Array {
constructor(...args) {
super(...args);
}
first() {
return this[0];
}
last() {
return this[this.length - 1];
}
}
const arr = new MyArray(1, 2, 3);
console.log(arr.first()); // 1
console.log(arr.last()); // 3
5. 静态方法和属性
静态方法
class MathUtils {
static add(a, b) {
return a + b;
}
static multiply(a, b) {
return a * b;
}
}
console.log(MathUtils.add(2, 3)); // 5
静态属性(ES2022)
class MyClass {
static version = '1.0.0';
static get DEFAULT_CONFIG() {
return { debug: false };
}
}
console.log(MyClass.version); // "1.0.0"
console.log(MyClass.DEFAULT_CONFIG); // { debug: false }
6. Getter 和 Setter
class User {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
set fullName(value) {
const parts = value.split(' ');
this.firstName = parts[0];
this.lastName = parts[1];
}
get age() {
return this._age || 0;
}
set age(value) {
if (value < 0) {
throw new Error('Age cannot be negative');
}
this._age = value;
}
}
const user = new User('John', 'Doe');
console.log(user.fullName); // "John Doe"
user.fullName = 'Jane Smith';
console.log(user.firstName); // "Jane"
console.log(user.lastName); // "Smith"
7. 私有属性和方法(ES2022)
class BankAccount {
// 私有属性
#balance = 0;
#accountNumber;
constructor(accountNumber, initialBalance = 0) {
this.#accountNumber = accountNumber;
this.#balance = initialBalance;
}
// 公共方法
deposit(amount) {
if (amount > 0) {
this.#balance += amount;
}
}
withdraw(amount) {
if (amount > 0 && amount <= this.#balance) {
this.#balance -= amount;
}
}
getBalance() {
return this.#balance;
}
// 私有方法
#validateAmount(amount) {
return typeof amount === 'number' && amount > 0;
}
// 私有 getter/setter
get #maskedAccountNumber() {
return `****${this.#accountNumber.slice(-4)}`;
}
getAccountInfo() {
return `Account: ${this.#maskedAccountNumber}, Balance: $${this.#balance}`;
}
}
const account = new BankAccount('123456789', 1000);
account.deposit(500);
console.log(account.getBalance()); // 1500
// console.log(account.#balance); // SyntaxError: Private field '#balance' must be declared in an enclosing class
8. 实例属性定义
传统方式
class Counter {
constructor() {
this.count = 0;
}
increment() {
this.count++;
}
}
现代方式(ES2022)
class Counter {
count = 0; // 实例属性
increment() {
this.count++;
}
// 箭头函数方法(自动绑定 this)
handleClick = () => {
this.increment();
}
}
9. Mixins 模式
// Mixin 工厂函数
const Flyable = (superclass) => class extends superclass {
fly() {
console.log('Flying...');
}
};
const Swimmable = (superclass) => class extends superclass {
swim() {
console.log('Swimming...');
}
};
// 使用 Mixins
class Animal {
constructor(name) {
this.name = name;
}
}
class Duck extends Swimmable(Flyable(Animal)) {
constructor(name) {
super(name);
}
quack() {
console.log('Quack!');
}
}
const duck = new Duck('Donald');
duck.fly(); // "Flying..."
duck.swim(); // "Swimming..."
duck.quack(); // "Quack!"
10. Class 表达式
// 匿名 Class 表达式
const MyClass = class {
constructor(name) {
this.name = name;
}
};
// 命名 Class 表达式
const MyClass = class NamedClass {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
};
// 立即执行 Class
const instance = new class {
constructor() {
this.name = 'Anonymous';
}
greet() {
return `Hello, ${this.name}`;
}
}();
console.log(instance.greet()); // "Hello, Anonymous"
11. Class 的 this 绑定
class Button {
constructor(element) {
this.element = element;
this.clickCount = 0;
// 箭头函数自动绑定 this
this.element.addEventListener('click', this.handleClick);
}
// 箭头函数方法
handleClick = () => {
this.clickCount++;
console.log(`Clicked ${this.clickCount} times`);
}
// 普通方法需要手动绑定
handleHover() {
console.log('Hovered');
}
}
// 使用
const button = new Button(document.getElementById('myButton'));
12. Class 与原型链
class Parent {
parentMethod() {
return 'parent';
}
}
class Child extends Parent {
childMethod() {
return 'child';
}
}
const child = new Child();
// 原型链关系
console.log(child.__proto__ === Child.prototype); // true
console.log(Child.prototype.__proto__ === Parent.prototype); // true
console.log(Parent.prototype.__proto__ === Object.prototype); // true
// 方法查找
console.log(child.childMethod()); // "child"
console.log(child.parentMethod()); // "parent"
13. 实际应用示例
// 完整的用户管理系统示例
class UserManager {
// 私有属性
#users = new Map();
#nextId = 1;
// 静态属性
static DEFAULT_ROLE = 'user';
// 实例属性
name = 'UserManager';
addUser(userData) {
const id = this.#nextId++;
const user = {
id,
...userData,
role: userData.role || UserManager.DEFAULT_ROLE,
createdAt: new Date()
};
this.#users.set(id, user);
return user;
}
getUser(id) {
return this.#users.get(id);
}
removeUser(id) {
return this.#users.delete(id);
}
getAllUsers() {
return Array.from(this.#users.values());
}
// 静态方法
static validateEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
// Getter
get userCount() {
return this.#users.size;
}
}
// 使用示例
const userManager = new UserManager();
const user = userManager.addUser({
name: 'Alice',
email: 'alice@example.com'
});
console.log(userManager.userCount); // 1
console.log(UserManager.validateEmail('test@example.com')); // true
当然可以!ES6 的 class 虽然让 JavaScript 的面向对象编程更清晰、更接近传统语言,但它本质上仍然是基于原型(prototype)的语法糖。如果不了解其底层机制和注意事项,很容易写出“看似正确但实际有问题”的代码。
使用注意事项
1. class 不是真正的“类” —— 它是语法糖
ES6 的 class 只是构造函数的语法糖,底层依然是原型链。
class Person {
constructor(name) {
this.name = name;
}
sayHello() {
console.log(`Hello, ${this.name}`);
}
}
// 等价于:
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function () {
console.log(`Hello, ${this.name}`);
};
注意:
class内部方法不会被枚举(enumerable: false),这是与手动添加方法的区别。
2. 必须使用 new 调用,否则报错
class Person {
constructor(name) {
this.name = name;
}
}
Person('Alice'); // TypeError: Class constructor Person cannot be invoked without 'new'
必须:
new Person('Alice')
3. 不存在变量提升(No Hoisting)
const p = new Person('Alice'); // ReferenceError
class Person {
constructor(name) {
this.name = name;
}
}
class声明不会提升,必须先声明再使用(类似let/const)。
4. 类内部默认严格模式
class Person {
constructor(name) {
// 自动启用严格模式
name = 'Alice'; // 正常
undeclared = 'test'; // ReferenceError,严格模式下不能隐式创建全局变量
}
}
好事,避免意外错误。
5. this 指向问题 —— 方法解构后丢失上下文
class Person {
constructor(name) {
this.name = name;
}
sayHello() {
console.log(`Hello, ${this.name}`);
}
}
const p = new Person('Alice');
p.sayHello(); // ✅ Hello, Alice
const { sayHello } = p;
sayHello(); // Hello, undefined(this 指向丢失)
解决方案:
方案 1:箭头函数绑定(在 constructor 中)**
class Person {
constructor(name) {
this.name = name;
this.sayHello = () => {
console.log(`Hello, ${this.name}`);
};
}
}
方案 2:手动绑定
const boundHello = p.sayHello.bind(p);
boundHello();
方案 3:使用 bind 在解构时
const { sayHello } = p;
sayHello.call(p);
6. 私有字段必须显式声明
class Person {
constructor(name) {
this.#name = name; // SyntaxError: Private field '#name' must be declared in an enclosing class
}
}
必须先声明:
class Person {
#name;
constructor(name) {
this.#name = name;
}
}
7. 静态方法不能访问实例属性/方法
class Person {
constructor(name) {
this.name = name;
}
static greet() {
console.log(`Hello, ${this.name}`); // this 指向类,不是实例
}
}
静态方法只能访问静态属性或传入的参数。
8. 继承时必须调用 super()
class Student extends Person {
constructor(name, grade) {
// 必须先调用 super()
this.grade = grade; // ReferenceError
super(name);
}
}
正确:
class Student extends Person {
constructor(name, grade) {
super(name); // 必须先调用
this.grade = grade;
}
}
9. super 的使用限制
super必须在constructor中调用super()才能访问thissuper.method()可以调用父类方法super不能单独使用(如console.log(super))
10. class 不能枚举,但方法可以被继承
for (let key in Person) {
console.log(key); // 不会输出任何东西(静态方法除外)
}
实例方法在原型链上,可被继承。
11. getter/setter 是属性,不是方法
obj.prop(); // 错误,不能加 ()
obj.prop = value; // 正确
12. 不要在 getter 中有副作用
get name() {
this.#visitCount++; // 不该在 getter 中修改状态
return this.#name;
}
getter 应该是纯读取操作。
最佳实践总结
| 场景 | 建议 |
|---|---|
| 定义类 | 使用 class + constructor |
| 私有数据 | 使用 #privateField |
| 封装访问 | get/set + 私有字段 |
| 方法绑定 | 在 constructor 中用箭头函数或 bind |
| 继承 | extends + super() |
| 静态成员 | 用 static,只访问静态数据 |
| 调用方式 | 必须 new |
| 命名 | 避免与 getter/setter 同名 |
| 调试 | 注意 this 指向和 super 调用 |
一句话总结:
ES6class让代码更清晰,但本质仍是原型继承。使用时要注意:
- 必须
newthis可能丢失- 私有字段需声明
- 继承要调
super- getter/setter 是属性
总结
ES6 Class 的主要优势:
- 语法更清晰:更接近传统面向对象语言
- 更好的封装:支持私有属性和方法
- 继承更简单:使用
extends和super关键字 - 静态成员:支持静态方法和属性
- Getter/Setter:提供属性访问控制
- 更好的工具支持:IDE 和调试工具支持更好
更多推荐


所有评论(0)