存取器(getter/setter)
存取器基础概念
TypeScript中的存取器允许通过get和set关键字来控制对对象成员的访问。这种方式提供了更精细的属性访问控制,比直接暴露字段更安全、更灵活。存取器本质上是一种特殊的方法,但在语法上看起来像普通属性。
class Person {
private _age: number;
get age(): number {
return this._age;
}
set age(value: number) {
if (value < 0) {
throw new Error("年龄不能为负数");
}
this._age = value;
}
}
const person = new Person();
person.age = 25; // 调用set age(25)
console.log(person.age); // 调用get age()
存取器与普通属性的区别
存取器虽然使用起来像属性,但与直接定义的属性有本质区别:
- 计算属性:getter可以返回计算后的值
- 验证逻辑:setter可以加入验证逻辑
- 访问控制:可以只提供getter实现只读属性
- 延迟加载:可以在getter中实现延迟初始化
class Circle {
private _radius: number;
private _area: number | null = null;
constructor(radius: number) {
this._radius = radius;
}
get radius(): number {
return this._radius;
}
set radius(value: number) {
if (value <= 0) {
throw new Error("半径必须为正数");
}
this._radius = value;
this._area = null; // 清除缓存
}
get area(): number {
if (this._area === null) {
this._area = Math.PI * this._radius ** 2;
}
return this._area;
}
}
存取器的高级用法
只读属性
通过只定义getter不定义setter,可以创建只读属性:
class Configuration {
private readonly _apiUrl: string;
constructor(apiUrl: string) {
this._apiUrl = apiUrl;
}
get apiUrl(): string {
return this._apiUrl;
}
}
const config = new Configuration("https://api.example.com");
// config.apiUrl = "new-url"; // 错误: 无法分配到只读属性
存取器继承
存取器可以像普通方法一样被继承和重写:
class Animal {
private _name: string;
constructor(name: string) {
this._name = name;
}
get name(): string {
return this._name;
}
set name(value: string) {
this._name = value;
}
}
class Dog extends Animal {
get name(): string {
return super.name + " (狗)";
}
}
const dog = new Dog("旺财");
console.log(dog.name); // 输出: 旺财 (狗)
存取器与接口
接口可以定义存取器的契约:
interface IUser {
username: string;
get fullName(): string;
set fullName(value: string);
}
class User implements IUser {
private _fullName: string = "";
username: string;
constructor(username: string) {
this.username = username;
}
get fullName(): string {
return this._fullName;
}
set fullName(value: string) {
if (value.length < 3) {
throw new Error("全名至少3个字符");
}
this._fullName = value;
}
}
存取器在框架中的应用
许多前端框架利用存取器实现响应式数据绑定:
class Observable<T> {
private _value: T;
private _subscribers: Array<(value: 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._subscribers.forEach(callback => callback(newValue));
}
}
subscribe(callback: (value: T) => void): void {
this._subscribers.push(callback);
}
}
const temperature = new Observable<number>(20);
temperature.subscribe(value => {
console.log(`温度变更为: ${value}°C`);
});
temperature.value = 25; // 触发订阅回调
存取器性能考虑
虽然存取器提供了很多便利,但也需要注意性能问题:
- 避免复杂计算:getter中避免执行耗时操作
- 缓存结果:对于计算成本高的值,考虑缓存
- 减少触发频率:setter中避免频繁触发副作用
class ExpensiveComputation {
private _input: number = 0;
private _cachedResult: number | null = null;
get input(): number {
return this._input;
}
set input(value: number) {
if (this._input !== value) {
this._input = value;
this._cachedResult = null; // 输入变化时清除缓存
}
}
get result(): number {
if (this._cachedResult === null) {
console.log("执行复杂计算...");
// 模拟耗时计算
this._cachedResult = this._input * this._input;
}
return this._cachedResult;
}
}
存取器与装饰器结合
TypeScript装饰器可以与存取器结合,实现更强大的功能:
function logAccess(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalGet = descriptor.get;
const originalSet = descriptor.set;
if (originalGet) {
descriptor.get = function() {
console.log(`获取 ${propertyKey}`);
return originalGet.apply(this);
};
}
if (originalSet) {
descriptor.set = function(value: any) {
console.log(`设置 ${propertyKey} 为 ${value}`);
originalSet.call(this, value);
};
}
}
class Product {
private _price: number = 0;
@logAccess
get price(): number {
return this._price;
}
@logAccess
set price(value: number) {
this._price = value;
}
}
const product = new Product();
product.price = 100; // 控制台输出: 设置 price 为 100
console.log(product.price); // 控制台输出: 获取 price
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn