带有属性装饰器的 TypeScript 类就像静态一样

TypeScript class with property decorator acts as if static

本文关键字:静态 一样 属性 TypeScript      更新时间:2023-09-26

我写了一个带有属性装饰器的类,当设置装饰属性时,该类会在类中设置一个标志。我还希望能够从类的一个实例复制到另一个实例。问题是,当我在一个对象上设置属性的值时,另一个对象上的属性值也会更改,就好像属性是静态的一样。我是JavaScript和TypeScript的新手。我错过了什么?

运行下面的文本代码将记录:

Setting propNum from undefined to 0
testclass.ts:18 Setting propNum from 0 to 123
test.spec.ts:13 t1.propNum = 123
test.spec.ts:14 t2.propNum = 123

t1.propNum 仍应为零

装饰

//
// property decorator to set dirty flag automatically for any decorated property
//
function testProperty( target: any, key: string ) {
    // property value
    var _val = this[key];
    // property getter
    function getter() {
        return _val;
    };
    // property setter
    function setter( newVal ) {
        if ( _val != newVal ) {
            console.log( `Setting ${key} from ${_val} to ${newVal}` );
            _val = newVal;
            this._dirty = true;
        }
    };
    //
    // Delete original property and define new property with getter & setter
    //
    if ( delete this[key] ) {
        // Create new property with getter and setter
        Object.defineProperty( target, key, {
            get: getter,
            set: setter,
            enumerable: true,
            configurable: true
        });
    }
}

测试类

export class TestClass {
    private _dirty: boolean;
    @testProperty
    public propNum: number = 0;

    constructor() {
        this._dirty = false;
    }
    public copyFrom( tc: TestClass ) {
        this.propNum = tc.propNum;
    }
}

测试代码

describe( 'Copy Class Test', () => {
    it( 'Copy Test', () => {
        var t1 = new TestClass();
        var t2 = new TestClass();
        t2.propNum = 123;
        console.log( `t1.propNum = ${t1.propNum}` );
        console.log( `t2.propNum = ${t2.propNum}` );
        expect( t1.propNum ).toBe( 0 );
        t1.copyFrom( t2 );
        expect( t1.propNum ).toBe( 123 );
    });
});

这里的主要问题是 getter 和 setter 共享同一个变量,而不是基于实例获取值。

这基本上与这样做相同:

function TestClass() {
}
var value;
Object.defineProperty(TestClass.prototype, "propNum", {
    get: function() { return value; },
    set: function(val) { value = val },
    enumerable: true,
    configurable: true
});

这会导致这种情况发生:

var a = new TestClass(), b = new TestClass();
a.propNum = 2;
a.propNum === b.propNum; // true, because they're both referencing the same variable

第二个问题是this[key]引用全局对象的属性。

您可能想要做的是以下几行(未经测试的代码):

function testProperty( target: Object, key: string ) {
    const privateKey = "_" + key;
    function getter() {
        return this[privateKey];
    }
    function setter( newVal: any ) {
        if ( this[privateKey] != newVal ) {
            console.log( `Setting ${key} from ${this[privateKey]} to ${newVal}` );
            this[privateKey] = newVal;
            this._dirty = true;
        }
    }
    Object.defineProperty( target, key, {
        get: getter,
        set: setter,
        enumerable: true,
        configurable: true
    });
}