阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 存取器(getter/setter)

存取器(getter/setter)

作者:陈川 阅读数:13261人阅读 分类: TypeScript

存取器基础概念

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()

存取器与普通属性的区别

存取器虽然使用起来像属性,但与直接定义的属性有本质区别:

  1. 计算属性:getter可以返回计算后的值
  2. 验证逻辑:setter可以加入验证逻辑
  3. 访问控制:可以只提供getter实现只读属性
  4. 延迟加载:可以在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; // 触发订阅回调

存取器性能考虑

虽然存取器提供了很多便利,但也需要注意性能问题:

  1. 避免复杂计算:getter中避免执行耗时操作
  2. 缓存结果:对于计算成本高的值,考虑缓存
  3. 减少触发频率: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

上一篇:静态成员与实例成员

下一篇:参数属性

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌