js的使用方法

JavaScript 类的完整写法指南

1. ES5 及之前的类写法

构造函数模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}

// 原型方法
Person.prototype.sayHello = function() {
return `Hello, my name is ${this.name}`;
};

// 静态方法
Person.createAnonymous = function() {
return new Person('Anonymous', 0);
};

// 使用
const person1 = new Person('John', 25);
console.log(person1.sayHello()); // Hello, my name is John

工厂函数模式

1
2
3
4
5
6
7
8
9
10
11
function createPerson(name, age) {
return {
name: name,
age: age,
sayHello: function() {
return `Hello, my name is ${this.name}`;
}
};
}

const person2 = createPerson('Alice', 30);

模块模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var Calculator = (function() {
// 私有变量
var precision = 2;

// 私有方法
function round(value) {
return Number(value.toFixed(precision));
}

// 返回公共接口
return {
add: function(a, b) {
return round(a + b);
},
subtract: function(a, b) {
return round(a - b);
},
setPrecision: function(p) {
precision = p;
}
};
})();

2. ES6 类写法

基础类定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class Person {
// 构造函数
constructor(name, age) {
this.name = name;
this.age = age;
this.createdAt = new Date();
}

// 实例方法
sayHello() {
return `Hello, my name is ${this.name}`;
}

getBirthYear() {
const currentYear = new Date().getFullYear();
return currentYear - this.age;
}

// 静态方法
static createAnonymous() {
return new Person('Anonymous', 0);
}

// Getter
get description() {
return `${this.name} is ${this.age} years old`;
}

// Setter
set nickname(value) {
if (value.length > 2) {
this._nickname = value;
}
}

get nickname() {
return this._nickname || 'No nickname';
}
}

// 使用
const person = new Person('John', 25);
console.log(person.sayHello());
console.log(Person.createAnonymous());

继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Student extends Person {
constructor(name, age, studentId, major) {
super(name, age); // 调用父类构造函数
this.studentId = studentId;
this.major = major;
this.grades = [];
}

// 重写父类方法
sayHello() {
return `Hello, I'm ${this.name}, a ${this.major} student`;
}

// 子类特有方法
addGrade(grade) {
this.grades.push(grade);
}

getGPA() {
if (this.grades.length === 0) return 0;
const sum = this.grades.reduce((total, grade) => total + grade, 0);
return (sum / this.grades.length).toFixed(2);
}

// 静态方法继承
static createTransferStudent(name, age, studentId, major, previousSchool) {
const student = new Student(name, age, studentId, major);
student.previousSchool = previousSchool;
return student;
}
}

// 使用
const student = new Student('Alice', 20, 'S12345', 'Computer Science');
student.addGrade(85);
student.addGrade(92);
console.log(student.getGPA()); // 88.50

3. 类的访问控制

私有字段和方法 (ES2022)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class BankAccount {
// 私有字段(以 # 开头)
#balance = 0;
#accountNumber;

// 私有方法
#generateAccountNumber() {
return 'ACC' + Math.random().toString(36).substr(2, 9).toUpperCase();
}

constructor(initialBalance = 0) {
this.#accountNumber = this.#generateAccountNumber();
this.#balance = initialBalance;
this.owner = '';
}

// 公共方法
deposit(amount) {
if (amount > 0) {
this.#balance += amount;
return true;
}
return false;
}

withdraw(amount) {
if (amount > 0 && amount <= this.#balance) {
this.#balance -= amount;
return amount;
}
return 0;
}

// 只能通过方法访问私有字段
getBalance() {
return this.#balance;
}

getAccountNumber() {
return this.#accountNumber;
}
}

// 使用
const account = new BankAccount(1000);
account.deposit(500);
console.log(account.getBalance()); // 1500
// console.log(account.#balance); // 错误:私有字段不能在类外访问

4. 高级类特性

抽象基类模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class Shape {
constructor(color = 'black') {
if (new.target === Shape) {
throw new Error('Shape is an abstract class and cannot be instantiated directly');
}
this.color = color;
}

// 抽象方法(子类必须实现)
calculateArea() {
throw new Error('Method "calculateArea" must be implemented');
}

// 抽象方法
calculatePerimeter() {
throw new Error('Method "calculatePerimeter" must be implemented');
}

// 具体方法
describe() {
return `A ${this.color} shape with area ${this.calculateArea()}`;
}
}

class Circle extends Shape {
constructor(radius, color) {
super(color);
this.radius = radius;
}

calculateArea() {
return Math.PI * this.radius * this.radius;
}

calculatePerimeter() {
return 2 * Math.PI * this.radius;
}
}

class Rectangle extends Shape {
constructor(width, height, color) {
super(color);
this.width = width;
this.height = height;
}

calculateArea() {
return this.width * this.height;
}

calculatePerimeter() {
return 2 * (this.width + this.height);
}
}

Mixin 模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// Mixin 函数
const CanSpeak = (Base) => class extends Base {
speak(message) {
console.log(`${this.name} says: ${message}`);
}
};

const CanWalk = (Base) => class extends Base {
walk() {
console.log(`${this.name} is walking`);
}
};

const CanSwim = (Base) => class extends Base {
swim() {
console.log(`${this.name} is swimming`);
}
};

// 使用 Mixin
class Animal {
constructor(name) {
this.name = name;
}
}

class Dog extends CanWalk(CanSpeak(Animal)) {
bark() {
console.log('Woof! Woof!');
}
}

class Duck extends CanWalk(CanSwim(CanSpeak(Animal))) {
quack() {
console.log('Quack! Quack!');
}
}

const dog = new Dog('Buddy');
dog.speak('Hello!'); // Buddy says: Hello!
dog.walk(); // Buddy is walking

const duck = new Duck('Donald');
duck.speak('Hi!'); // Donald says: Hi!
duck.walk(); // Donald is walking
duck.swim(); // Donald is swimming

5. 类的装饰器(实验性特性)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 简单的装饰器函数
function log(target, name, descriptor) {
const original = descriptor.value;
if (typeof original === 'function') {
descriptor.value = function(...args) {
console.log(`Calling ${name} with`, args);
try {
const result = original.apply(this, args);
console.log(`Result:`, result);
return result;
} catch (e) {
console.log(`Error:`, e);
throw e;
}
};
}
return descriptor;
}

function readonly(target, name, descriptor) {
descriptor.writable = false;
return descriptor;
}

class MathOperations {
@log
static add(a, b) {
return a + b;
}

@readonly
static version = '1.0.0';
}

// 使用
MathOperations.add(5, 3);
// 输出: Calling add with [5, 3]
// 输出: Result: 8

// MathOperations.version = '2.0.0'; // 错误:只读属性

6. 类的迭代器和生成器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class Collection {
constructor() {
this.items = [];
}

add(item) {
this.items.push(item);
}

// 迭代器协议
*[Symbol.iterator]() {
for (let item of this.items) {
yield item;
}
}

// 生成器方法
*filter(predicate) {
for (let item of this.items) {
if (predicate(item)) {
yield item;
}
}
}

// 反向迭代器
*reverse() {
for (let i = this.items.length - 1; i >= 0; i--) {
yield this.items[i];
}
}
}

// 使用
const collection = new Collection();
collection.add(1);
collection.add(2);
collection.add(3);
collection.add(4);

// 使用 for...of 循环
for (let item of collection) {
console.log(item); // 1, 2, 3, 4
}

// 使用扩展运算符
console.log([...collection]); // [1, 2, 3, 4]

// 使用过滤
for (let item of collection.filter(x => x > 2)) {
console.log(item); // 3, 4
}

7. 类的属性验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class ValidatedPerson {
constructor(name, age, email) {
this.name = this._validateName(name);
this.age = this._validateAge(age);
this.email = this._validateEmail(email);
}

_validateName(name) {
if (typeof name !== 'string' || name.length < 2) {
throw new Error('Name must be a string with at least 2 characters');
}
return name;
}

_validateAge(age) {
if (typeof age !== 'number' || age < 0 || age > 150) {
throw new Error('Age must be a number between 0 and 150');
}
return age;
}

_validateEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
throw new Error('Invalid email format');
}
return email;
}

// 使用 Proxy 进行属性设置验证
static createStrict() {
return new Proxy(new ValidatedPerson('', 0, ''), {
set(target, property, value) {
const validators = {
name: (val) => typeof val === 'string' && val.length >= 2,
age: (val) => typeof val === 'number' && val >= 0 && val <= 150,
email: (val) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val)
};

if (validators[property] && !validators[property](value)) {
throw new Error(`Invalid value for ${property}`);
}

target[property] = value;
return true;
}
});
}
}

8. 实际应用示例

简单的状态管理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
class Store {
constructor(initialState = {}) {
this.state = { ...initialState };
this.listeners = [];
this.actions = {};
}

// 获取状态
getState() {
return { ...this.state };
}

// 设置状态
setState(newState) {
this.state = { ...this.state, ...newState };
this._notifyListeners();
}

// 订阅状态变化
subscribe(listener) {
this.listeners.push(listener);
return () => {
this.listeners = this.listeners.filter(l => l !== listener);
};
}

// 注册动作
registerAction(name, action) {
this.actions[name] = (...args) => {
const newState = action(this.getState(), ...args);
this.setState(newState);
};
}

// 分发动作
dispatch(actionName, ...args) {
if (this.actions[actionName]) {
this.actions[actionName](...args);
}
}

_notifyListeners() {
this.listeners.forEach(listener => listener(this.getState()));
}
}

// 使用
const store = new Store({ count: 0, user: null });

store.registerAction('increment', (state) => ({
...state,
count: state.count + 1
}));

store.registerAction('setUser', (state, user) => ({
...state,
user
}));

store.subscribe((state) => {
console.log('State changed:', state);
});

store.dispatch('increment');
store.dispatch('setUser', { name: 'John', age: 25 });

总结

选择类写法的建议:

  1. 现代项目:使用 ES6+ 类语法,支持私有字段和静态方法
  2. 兼容性要求高:使用 ES5 构造函数 + 原型模式
  3. 需要多重继承:使用 Mixin 模式
  4. 需要严格封装:使用私有字段 (#) 和验证
  5. 函数式编程风格:考虑使用工厂函数

最佳实践:

  • 使用有意义的类名(PascalCase)
  • 保持类的单一职责
  • 合理使用继承和组合
  • 对输入参数进行验证
  • 使用合适的访问控制
  • 为公共方法提供文档注释