假设我有一个定义如下的类:
class MyClass {
constructor(a, b) {
this._a = a;
this._b = b;
}
get a() {
return this._a;
}
set a(val) {
this._a = val;
}
add() {
return this._a + this._b;
}
}
我希望能够在运行时直接访问和操作 getter 和 setter 函数,以便将它们包装在额外的调试代码中。使用“添加”功能,我可以这样做:
let oldAdd = MyClass.prototype.add;
MyClass.prototype.add = function() {
console.log('add called!');
let result = oldAdd.call(this);
console.log('add result: ' + result);
return result;
}
但是,我找不到以类似方式修改 getter 和 setter 函数的方法。
我已经尝试过
let propDef = Reflect.getOwnPropertyDescriptor(MyClass.prototype, 'a');
propDef.get = function() {
// ...
}
但此更改实际上并未应用。
有什么想法吗?
我也有兴趣知道是否可以以相同的方式访问和修改构造函数。
最佳答案
是的,您可以通过重新配置属性来做到这一点。这是一个例子(见评论):
class MyClass {
constructor(a, b) {
this._a = a;
this._b = b;
}
get a() {
return this._a;
}
set a(val) {
this._a = val;
}
add() {
return this._a + this._b;
}
}
// Use it before redefining
const instance = new MyClass(1, 2);
console.log(instance.a); // 1
instance.a = 2;
console.log(instance.a); // 2
// Redefine the property
const desc = Reflect.getOwnPropertyDescriptor(MyClass.prototype, "a");
const {get: originalGet, set: originalSet} = desc;
desc.get = function() {
const value = originalGet.call(this);
console.log("Debugging 'get' here, a's value is " + value);
return value;
};
desc.set = function(newValue) {
console.log("Debugging 'set' here, a's new value is " + newValue);
originalSet.call(this, newValue);
};
Object.defineProperty(MyClass.prototype, "a", desc);
// Use it after redefining
console.log(instance.a); // 2, after seeing console statement
instance.a = 3; // Triggers another console statement
console.log(instance.a); // 3 (w/ console statement)
您所做的不起作用的部分原因是您从未在对象上设置新的描述符。您返回的描述符不提供对对象内定义的直接访问,它只是由 getOwnPropertyDescriptor
创建的新对象。为了使更改生效,您需要设置新的描述符。
您在下面询问了有关对 MyClass
本身执行相同操作的问题。正如您所指出的,除了替换函数之外,我们还需要确保其属性显示在替换函数上。
一个简单、容易的方法是让新函数继承旧函数:
const originalConstructor = MyClass;
MyClass = class MyClass extends originalConstructor {
constructor(...args) {
return new originalConstructor(...args);
}
};
一个鲜为人知的事实是,当 B
扩展 A
时,会发生两件事:
B.prototype
的原型(prototype)设置为A.prototype
(这是众所周知的),并且B
的原型(prototype)设置为A
(不太知名)
因此,A
上的任何静态数据都可以通过继承在 B
上使用。
所以:
class Base {
feature() {}
static staticFeature() {}
}
class MyClass extends Base {
subFeature() {}
static staticSubFeature() {}
}
const originalConstructor = MyClass;
MyClass = class MyClass extends originalConstructor {
constructor(...args) {
return new originalConstructor(...args);
}
};
console.log(typeof MyClass.staticFeature);
console.log(typeof MyClass.staticSubFeature);
const instance = new MyClass();
console.log(typeof instance.feature);
console.log(typeof instance.subFeature);
或者,如果您想要一个真正的高保真副本,您可以从 MyClass
的原型(prototype)派生,然后复制任何 MyClass
自己的属性;但它更复杂:
const originalConstructor = MyClass;
MyClass = class MyClass extends Object.getPrototypeOf(originalConstructor) {
constructor(...args) {
return new originalConstructor(...args);
}
};
Object.getOwnPropertyNames(originalConstructor)
.concat(Object.getOwnPropertySymbols(originalConstructor))
.forEach(name => {
MyClass[name] = originalConstructor[name];
});
示例:
class Base {
feature() {}
static staticFeature() {}
}
class MyClass extends Base {
subFeature() {}
static staticSubFeature() {}
}
const originalConstructor = MyClass;
MyClass = class MyClass extends Object.getPrototypeOf(originalConstructor) {
constructor(...args) {
return new originalConstructor(...args);
}
};
Object.getOwnPropertyNames(originalConstructor)
.concat(Object.getOwnPropertySymbols(originalConstructor))
.forEach(name => {
MyClass[name] = originalConstructor[name];
});
console.log(typeof MyClass.staticFeature);
console.log(typeof MyClass.staticSubFeature);
const instance = new MyClass();
console.log(typeof instance.feature);
console.log(typeof instance.subFeature);
关于javascript - 在 JavaScript 中,特别是 NodeJS 上的 ES6,我可以在运行时直接操作类的 getter 和 setter 函数吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42110019/