引言

在面向对象编程中,封装是一个核心概念。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. 封装控制:隐藏内部实现细节

  2. 验证逻辑:在设置值时添加验证

  3. 计算属性:动态计算返回值

  4. 访问控制:创建只读或只写属性

  5. 副作用:可以在值变化时执行额外操作

高级用法

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())
语义 表示属性访问 表示操作/行为
可链式调用 不适合 适合
复杂度 应保持简单 可以复杂

最佳实践

  1. 保持简单:Getter/Setter 应该简单快速,避免复杂逻辑

  2. 命名一致:使用一致的命名约定(如 _ 前缀表示私有字段)

  3. 避免副作用:Getter 不应该修改状态,Setter 应该只修改相关状态

  4. 性能考虑:计算密集型操作不适合放在 getter 中

  5. 文档注释:为重要的 getter/setter 添加文档注释

常见陷阱

  1. 无限递归

// 错误示例 - 无限递归
set value(v: number) {
    this.value = v; // 会不断调用 setter 本身
}

正确做法:

private _value: number;
set value(v: number) {
    this._value = v;
}
  1. 意外的副作用

// 不良实践 - 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

  • 实现响应式编程模式

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐