阿里云主机折上折
  • 微信号
Current Site:Index > Accessors (getter/setter)

Accessors (getter/setter)

Author:Chuan Chen 阅读数:13262人阅读 分类: TypeScript

Basic Concepts of Accessors

Accessors in TypeScript allow control over access to object members through the get and set keywords. This approach provides finer-grained property access control, making it safer and more flexible than directly exposing fields. Accessors are essentially special methods but syntactically resemble regular properties.

class Person {
    private _age: number;

    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;  // Calls set age(25)
console.log(person.age);  // Calls get age()

Differences Between Accessors and Regular Properties

Although accessors are used like properties, they differ fundamentally from directly defined properties:

  1. Computed Properties: Getters can return computed values.
  2. Validation Logic: Setters can include validation logic.
  3. Access Control: Only providing a getter can create read-only properties.
  4. Lazy Loading: Getters can implement lazy initialization.
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("Radius must be positive");
        }
        this._radius = value;
        this._area = null; // Clear cache
    }

    get area(): number {
        if (this._area === null) {
            this._area = Math.PI * this._radius ** 2;
        }
        return this._area;
    }
}

Advanced Usage of Accessors

Read-Only Properties

By defining only a getter without a setter, you can create read-only properties:

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"; // Error: Cannot assign to read-only property

Accessor Inheritance

Accessors can be inherited and overridden like regular methods:

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 + " (Dog)";
    }
}

const dog = new Dog("旺财");
console.log(dog.name); // Output: 旺财 (Dog)

Accessors and Interfaces

Interfaces can define contracts for accessors:

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("Full name must be at least 3 characters");
        }
        this._fullName = value;
    }
}

Accessors in Frameworks

Many frontend frameworks use accessors to implement reactive data binding:

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(`Temperature changed to: ${value}°C`);
});
temperature.value = 25; // Triggers subscription callback

Performance Considerations for Accessors

While accessors offer many conveniences, performance considerations are important:

  1. Avoid Complex Computations: Avoid time-consuming operations in getters.
  2. Cache Results: For values with high computation costs, consider caching.
  3. Reduce Trigger Frequency: Avoid frequent side effects in setters.
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; // Clear cache when input changes
        }
    }

    get result(): number {
        if (this._cachedResult === null) {
            console.log("Performing complex computation...");
            // Simulate time-consuming computation
            this._cachedResult = this._input * this._input;
        }
        return this._cachedResult;
    }
}

Combining Accessors with Decorators

TypeScript decorators can be combined with accessors to achieve more powerful functionality:

function logAccess(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalGet = descriptor.get;
    const originalSet = descriptor.set;

    if (originalGet) {
        descriptor.get = function() {
            console.log(`Getting ${propertyKey}`);
            return originalGet.apply(this);
        };
    }

    if (originalSet) {
        descriptor.set = function(value: any) {
            console.log(`Setting ${propertyKey} to ${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; // Console output: Setting price to 100
console.log(product.price); // Console output: Getting price

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

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

下一篇:参数属性

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.