필드를 게으른 것으로 선언
TypeScript에서 필드가 게으르게 초기화되었다고 선언하는 구문이 있습니까?
스칼라에 있는 것처럼, 예를 들어:
lazy val f1 = new Family("Stevens")
필드에 처음 액세스할 때만 필드 이니셜라이저가 실행됨을 의미합니다.
더 나은 방법이 필요합니다.
class Lazy {
private _f1;
get f1() {
return this._f1 || (this._f1 = expensiveInitializationForF1());
}
}
네, 장식가와 함께 이 문제를 해결할 수 있지만, 단순한 경우에는 과잉 살상이 될 수 있습니다.
사용할 수 없습니다.@lazyInitialize
직접 타자기로 쳐주세요.그래서 당신은 그것을 다시 써야 합니다.여기 제 장식가가 있습니다. 당신은 그것을 복사해서 사용하기만 하면 됩니다. 대신에 부동산이 아닌 게터에서 @filename을 사용하세요.
@지옥의
const {defineProperty, getPrototypeOf}=Object;
export default function lazy(target, name, {get:initializer, enumerable, configurable, set:setter}: PropertyDescriptor={}): any {
const {constructor}=target;
if (initializer === undefined) {
throw `@lazy can't be set as a property \`${name}\` on ${constructor.name} class, using a getter instead!`;
}
if (setter) {
throw `@lazy can't be annotated with get ${name}() existing a setter on ${constructor.name} class!`;
}
function set(that, value) {
if (value === undefined) {
value = that;
that = this;
}
defineProperty(that, name, {
enumerable: enumerable,
configurable: configurable,
value: value
});
return value;
}
return {
get(){
if (this === target) {
return initializer;
}
//note:subclass.prototype.foo when foo exists in superclass nor subclass,this will be called
if (this.constructor !== constructor && getPrototypeOf(this).constructor === constructor) {
return initializer;
}
return set(this, initializer.call(this));
},
set
};
}
시험
describe("@lazy", () => {
class Foo {
@lazy get value() {
return new String("bar");
}
@lazy
get fail(): string {
throw new Error("never be initialized!");
}
@lazy get ref() {
return this;
}
}
it("initializing once", () => {
let foo = new Foo();
expect(foo.value).toEqual("bar");
expect(foo.value).toBe(foo.value);
});
it("could be set @lazy fields", () => {
//you must to set object to any
//because typescript will infer it by static ways
let foo: any = new Foo();
foo.value = "foo";
expect(foo.value).toEqual("foo");
});
it("can't annotated with fields", () => {
const lazyOnProperty = () => {
class Bar {
@lazy bar: string = "bar";
}
};
expect(lazyOnProperty).toThrowError(/@lazy can't be set as a property `bar` on Bar class/);
});
it("get initializer via prototype", () => {
expect(typeof Foo.prototype.value).toBe("function");
});
it("calling initializer will be create an instance at a time", () => {
let initializer: any = Foo.prototype.value;
expect(initializer.call(this)).toEqual("bar");
expect(initializer.call(this)).not.toBe(initializer.call(this));
});
it("ref this correctly", () => {
let foo = new Foo();
let ref: any = Foo.prototype.ref;
expect(this).not.toBe(foo);
expect(foo.ref).toBe(foo);
expect(ref.call(this)).toBe(this);
});
it("discard the initializer if set fields with other value", () => {
let foo: any = new Foo();
foo.fail = "failed";
expect(foo.fail).toBe("failed");
});
it("inherit @lazy field correctly", () => {
class Bar extends Foo {
}
const assertInitializerTo = it => {
let initializer: any = Bar.prototype.ref;
let initializer2: any = Foo.prototype.ref;
expect(typeof initializer).toBe("function");
expect(initializer.call(it)).toBe(it);
expect(initializer2.call(it)).toBe(it);
};
assertInitializerTo(this);
let bar = new Bar();
assertInitializerTo({});
expect(bar.value).toEqual("bar");
expect(bar.value).toBe(bar.value);
expect(bar.ref).toBe(bar);
assertInitializerTo(this);
});
it("overriding @lazy field to discard super.initializer", () => {
class Bar extends Foo {
get fail() {
return "error";
};
}
let bar = new Bar();
expect(bar.fail).toBe("error");
});
it("calling super @lazy fields", () => {
let calls = 0;
class Bar extends Foo {
get ref(): any {
calls++;
//todo:a typescript bug:should be call `super.ref` getter instead of super.ref() correctly in typescript,but it can't
return (<any>super["ref"]).call(this);
};
}
let bar = new Bar();
expect(bar.ref).toBe(bar);
expect(calls).toBe(1);
});
it("throws errors if @lazy a property with setter", () => {
const lazyPropertyWithinSetter = () => {
class Bar{
@lazy
get bar(){return "bar";}
set bar(value){}
}
};
expect(lazyPropertyWithinSetter).toThrow(/@lazy can't be annotated with get bar\(\) existing a setter on Bar class/);
});
});
최신 버전, 클래스:
class Lazy<T> {
private #f: T | undefined;
constructor(#init: () => T) {}
public get f(): T {
return this.#f1 ??= this.#init();
}
}
최신 버전, 인라인:
let value;
// …
use_by_ref(value ??= lazy_init());
생성자에서 다른 값을 반환하는 것이 일반적으로 정의되지 않은 동작으로 간주되기 때문에 프록시가 있는 잠재적 미래 버전이 현재 작동하지 않습니다.
class Lazy<T> {
constructor(private init: { [K in keyof T]: () => T[K] }) {
let obj = Object.fromEntries(Object.keys(init).map(k => [k, undefined])) as unknown as { [K in keyof T]: undefined | T[K] };
Object.seal(obj);
return new Proxy(obj, this);
}
get<K extends keyof T>(t: T, k: K): T[K] {
return t[k] ??= this.init[k];
}
}
더 단순화할 수 있습니다.
다음과 같은 것을 사용하고 있습니다.
export interface ILazyInitializer<T> {(): T}
export class Lazy<T> {
private instance: T | null = null;
private initializer: ILazyInitializer<T>;
constructor(initializer: ILazyInitializer<T>) {
this.initializer = initializer;
}
public get value(): T {
if (this.instance == null) {
this.instance = this.initializer();
}
return this.instance;
}
}
let myObject: Lazy<MyObject>;
myObject = new Lazy(() => <MyObject>new MyObject("value1", "value2"));
const someString = myObject.value.getProp;
언급URL : https://stackoverflow.com/questions/42845543/declaring-a-field-as-lazy
'programing' 카테고리의 다른 글
선택하면 UITableViewCell에서 높이 변경을 애니메이션화할 수 있습니까? (0) | 2023.06.13 |
---|---|
Translate API를 사용하는 Python App Engine 앱에서 ImportError: No module name piclient.discovery라는 오류가 발생하는 이유는 무엇입니까? (0) | 2023.06.13 |
문자를 빼는 동작이 구체적인 이유는 무엇입니까? (0) | 2023.06.13 |
Angular 10 업그레이드 - 공통 수정JS 또는 AMD 종속성으로 인해 최적화 구제 조치가 발생할 수 있음 (0) | 2023.06.13 |
vue에서 자체 업데이트 정렬을 수행하는 방법 (0) | 2023.06.13 |