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

时间:2016-02-18 22:05:29

标签: javascript properties typescript decorator

我编写了一个带有属性装饰器的类,它在设置了装饰属性时在类中设置了一个标志。我还希望能够从该类的一个实例复制到另一个实例。问题是当我在一个对象上设置属性值时,另一个对象上的属性值也会发生变化,就好像属性是静态的一样。我是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 );
    });
});

1 个答案:

答案 0 :(得分:5)

这里的主要问题是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
    });
}