javascript - 在 JavaScript 中,特别是 NodeJS 上的 ES6,我可以在运行时直接操作类的 getter 和 setter 函数吗?

标签 javascript node.js ecmascript-6 getter-setter es6-class

假设我有一个定义如下的类:

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/

相关文章:

javascript - 如何查看包含的 JavaScript 源代码?

javascript - 无法从 Promise 函数接收正确的数据

javascript - (为什么)我不能从生成器中抛出异常吗?

angular - typescript :找不到名称 'Proxy'

javascript - Redux 表单 `event.target.value` 的 `onChange` 处理程序中的复选框的 `Field` 不正确

javascript - PHP上传文件上传后将动画gif变成普通图片

javascript - node.js 表单处理更新和删除

javascript - 尝试在 for 循环中执行 promise 链(对列表中的每个项目执行一次)——我该如何正确执行此操作?

javascript - 如何将函数参数转换为键/值对?

json - NodeJS 从流中反序列化