【发布时间】: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