【问题标题】:Using superclass methods in a superclass in JavaScript在 JavaScript 的超类中使用超类方法
【发布时间】:2022-11-04 08:11:57
【问题描述】:

JavaScript 有一个 OO 怪癖,即超类方法中的方法调用将调用子类方法。我发现我可以很容易地解决这个问题,除了构造函数。问题是在构造子类时,对象在调用super() 之前是不可用的。任何在子类中被重写的超类构造函数调用的方法都会找到一个未被子类初始化的对象。这是一个例子:

class Employee {
  constructor (name, group) {
    this.name = name;
    this.setGroup(group);
  }
  setGroup (group) {
    this.group = group;
  }
}

class Manager extends Employee {
  constructor (name, group, subordinates) {
    super(name, group);
    this.subordinates = subordinates.map(name => new Employee(name, group));
  }
  setGroup (group) {
    super.setGroup(group);
    this.subordinates.forEach(sub => sub.setGroup(group));
  }
}
const mgr = new Manager('Fred', 'R&D', ['Wilma', 'Barney']);

这将在 Employee.setGroup 中失败,因为 this.subordinates 尚未初始化。

一种解决方案是仅调用超类构造函数中的内部方法(例如 _setGroup()),并提供可以在子类中覆盖的公共包装器。但是,这很乏味,因为构造函数调用的任何方法也可以调用其他方法。

我想出了一个替代方案:


/**
 * Call a function that executes methods from this class, bypassing any
 * method in a subclass.
 * @param {Function} ctor - A class or Function constructor
 * @param {Object} self - An instance of the class
 * @param {Function} fn - A function to call. "this" will be set to self. Any method
 *  calls on self will ignore overriding methods in any subclass and use the
 *  ctor's methods.
 */
/* exported useClassMethods */
function useClassMethods (ctor, self, fn) {
    const subProto = Object.getPrototypeOf(self);
    // temporarily set the object prototype to this (super)class
    Object.setPrototypeOf(self, ctor.prototype);
    try {
        fn.call(self);
    } catch (error) {
        throw(error);
    } finally {
        // make sure the prototype is reset to the original value
        Object.setPrototypeOf(self, subProto);
    }
}

使用如下:

class Employee {
  constructor (name, group) {
    useClassMethods(Employee, this, () => {
      this.name = name;
      this.setGroup(group);
    })
  }
  setGroup (group) {
    this.group = group;
  }
}

这似乎可行,但中子在反应堆的这一部分非常热,我想知道是否有其他人有更好的解决方案或可以在其中挖洞。

【问题讨论】:

  • this.setGroup(group); 应该是 this.group = group;,因为您在构造函数中。创建实例后将分配方法
  • 这个例子是故意设计来说明这个问题的。
  • MDN 指出使用setPrototypeOf() 会降低对象性能。可能有一种方法可以克隆具有编辑过的原型链的对象,应用该功能,然后将其重新合并回原始对象,但这似乎有点冒险。
  • 在阅读this 之后,似乎使用setPrototypeOf() 的主要惩罚是使内联缓存无效。这在对象构造期间并不算太糟糕,这发生了一次。之后,内联缓存将通过正常使用重新建立。 useClassMethods() 不应由非构造方法使用,因为它可能会造成严重的性能损失。

标签: javascript javascript-objects


【解决方案1】:

我想出了一个更好的解决方案,它使用 Proxy 对象而不是操纵对象的原型:

function useClassMethods (ctor, obj, fn) {
    const classMethods = Object.create(ctor.prototype);
    const proxy = new Proxy(obj, {
        get: (target, prop) => (
            Reflect.get(target.hasOwnProperty(prop) ? target : classMethods, prop)
        ),
    })
    fn.call(proxy);
}

super 在子类和超类中都有效。唯一的限制是fn 不能是箭头函数,因此它使用传递的this 而不是词法this

class Employee {
  constructor (name, group) {
    useClassMethods(Employee, this, function () {
      this.name = name;
      this.setGroup(group);
    })
  }
  setGroup (group) {
    this.group = group;
  }
}

【讨论】:

    猜你喜欢
    • 2012-12-30
    • 2021-11-10
    • 2013-07-25
    • 1970-01-01
    • 2011-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多