TypeScript 中的 Getter 和 Setter:优雅的属性访问控制
·
引言
在面向对象编程中,封装是一个核心概念。TypeScript 作为 JavaScript 的超集,提供了完整的面向对象编程能力,其中 getter 和 setter 是实现封装的重要工具。本文将深入探讨 TypeScript 中 getter 和 setter 的用法、最佳实践以及常见应用场景。
什么是 Getter 和 Setter?
Getter 和 setter 是特殊的类方法,允许你像访问普通属性一样访问它们,但实际上它们是方法调用。这种特性在多种编程语言中都有实现,TypeScript 也不例外。
-
Getter:用于获取属性值
-
Setter:用于设置属性值
基本语法
1. 基本 Getter/Setter
class Person {
private _age: number = 0;
get age(): number {
return this._age;
}
set age(value: number) {
if (value < 0) {
throw new Error("Age cannot be negative");
}
this._age = value;
}
}
const person = new Person(); person.age = 25; // 调用 setter console.log(person.age); // 调用 getter,输出 25
2. 只读属性(只有 Getter)
class Circle {
private _radius: number;
constructor(radius: number) {
this._radius = radius;
}
get area(): number {
return Math.PI * this._radius ** 2;
}
}
const circle = new Circle(5);
console.log(circle.area); // 78.53981633974483
// circle.area = 100; // 错误: 没有 setter
为什么使用 Getter/Setter?
-
封装控制:隐藏内部实现细节
-
验证逻辑:在设置值时添加验证
-
计算属性:动态计算返回值
-
访问控制:创建只读或只写属性
-
副作用:可以在值变化时执行额外操作
高级用法
1. 基于其他属性的计算属性
class Rectangle {
constructor(private _width: number, private _height: number) {}
get width(): number { return this._width; }
set width(value: number) { this._width = value; }
get height(): number { return this._height; }
set height(value: number) { this._height = value; }
get area(): number {
return this._width * this._height;
}
}
2. 惰性求值(Lazy Evaluation)
class ExpensiveComputation {
private _result: number | null = null;
private _computed: boolean = false;
get result(): number {
if (!this._computed) {
console.log("Performing expensive computation...");
this._result = this.doExpensiveCalculation();
this._computed = true;
}
return this._result!;
}
private doExpensiveCalculation(): number {
// 模拟耗时计算
return Math.pow(10, 8) * Math.random();
}
}
3. 值变更观察
class ObservableValue<T> {
private _value: T;
private listeners: Array<(newValue: T) => void> = [];
constructor(initialValue: T) {
this._value = initialValue;
}
get value(): T {
return this._value;
}
set value(newValue: T) {
if (this._value !== newValue) {
this._value = newValue;
this.notifyListeners();
}
}
subscribe(listener: (newValue: T) => void): void {
this.listeners.push(listener);
}
private notifyListeners(): void {
this.listeners.forEach(listener => listener(this._value));
}
}
与普通方法的区别
| 特性 | Getter/Setter | 普通方法 |
|---|---|---|
| 调用方式 | 像属性一样访问 (obj.prop) |
需要括号 (obj.method()) |
| 语义 | 表示属性访问 | 表示操作/行为 |
| 可链式调用 | 不适合 | 适合 |
| 复杂度 | 应保持简单 | 可以复杂 |
最佳实践
-
保持简单:Getter/Setter 应该简单快速,避免复杂逻辑
-
命名一致:使用一致的命名约定(如
_前缀表示私有字段) -
避免副作用:Getter 不应该修改状态,Setter 应该只修改相关状态
-
性能考虑:计算密集型操作不适合放在 getter 中
-
文档注释:为重要的 getter/setter 添加文档注释
常见陷阱
-
无限递归:
// 错误示例 - 无限递归
set value(v: number) {
this.value = v; // 会不断调用 setter 本身
}
正确做法:
private _value: number;
set value(v: number) {
this._value = v;
}
-
意外的副作用:
// 不良实践 - getter 有副作用
get nextId(): number {
this._idCounter++; // 副作用
return this._idCounter;
}
TypeScript 特有特性
1. 接口中的 Getter/Setter
interface IUser {
get fullName(): string;
set fullName(name: string);
}
class User implements IUser {
private _fullName: string = "";
get fullName(): string {
return this._fullName;
}
set fullName(name: string) {
this._fullName = name;
}
}
2. 访问器修饰符
class AccessExample {
public get publicValue(): number { return 42; }
protected get protectedValue(): number { return 42; }
private get privateValue(): number { return 42; }
}
结论
TypeScript 中的 getter 和 setter 提供了强大的属性访问控制能力。合理使用它们可以:
-
提高代码的可维护性
-
增强数据验证
-
创建更直观的 API
-
实现响应式编程模式
更多推荐

所有评论(0)