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