如何避免ES6 Javascript类继承命名冲突

时间:2016-12-15 18:35:46

标签: javascript inheritance ecmascript-6 es6-class

如何避免在ES6 Javascript中命名来自类继承的冲突?

大型ES6 Javascript应用程序使用了大量的继承,因此在基类中使用通用名称可能意味着在创建派生类时稍后会调试头痛。这可能是糟糕的类设计的产物,但似乎更容易成为Javascript能够顺利扩展的问题。其他语言提供隐藏继承变量(Java)或属性(C#)的机制。缓解此问题的另一种方法是使用私有变量,这不是Javascript所具有的。

以下是此类碰撞的示例。 TreeObject类扩展了一个Evented对象(以继承了公平的功能),但它们都使用parent来存储它们的父级。

class Evented {
    constructor(parent) {
        this.parent = parent;
    }
}
class TreeObject extends Evented{
    constructor(eventParent, treeParent) {
        super(eventParent);
        this.parent = treeParent;
    }
}

虽然这个例子有点人为,但我在像Ember这样的大型库中遇到了类似的冲突,图书馆和最终应用程序之间的术语重叠了很多,导致我浪费时间在这里和那里浪费。

3 个答案:

答案 0 :(得分:6)

这似乎是一个设计问题(使用较小的对象和更扁平的层次结构),但也有一个解决您的问题的方法:符号

const parentKey = Symbol("parent");
export class Evented {
    constructor(parent) {
        this[parentKey] = parent;
    }
}

import {Evented} from "…"
const parentKey = Symbol("parent");
class TreeObject extends Evented {
    constructor(eventParent, treeParent) {
        super(eventParent);
        this[parentKey] = treeParent;
    }
}

它们可以可靠地防止任何碰撞,因为所有符号都是唯一的,无论它们的描述符如何。

答案 1 :(得分:2)

  

大型ES6 JavaScript应用程序使用大量继承。

实际上,他们中的大多数都没有。在像Angular这样的框架中,平台定义的类的一个级别的继承是最常见的。深度遗传结构是脆弱的,最好避免。一个大型应用程序可能有两个用户级别的类,A> B,其中A包含一堆通用逻辑,B1和B2是轻度特化,例如组件的不同模板,其级别不会引起对碰撞的担忧。

Evented的示例在语义上不是真正的父类;它更像是mixin的本质。由于JS并不能很好地处理mixins,而不是从Tree派生Evented,我将该对象作为属性保存:

class TreeObject {
    constructor(eventParent, treeParent) {
        this.evented = new Evented(eventParent);
        this.parent = treeParent;
    }
    send(msg) { this.evented.send(msg); }
}

如果你真的想设计Evented用作类似mixin的超类,那么设计师有责任使成员变量尽可能独特,如

export class Evented {
    constructor(parent) {
        this.eventedParent = parent;
    }
}

或使用符号,如另一个答案中所建议的那样。或者,考虑使用地图:

const parents = new Map();

class Evented {
  constructor(parent) {
    parents.set(this, parent);
  }
  sendParent(msg) {
    parents.get(this).send(msg);
  }
}
  

我在像Ember

这样的大型图书馆里遇到了类似的碰撞

我没有。 Ember类通常定义很少的成员,他们定义的方法定义明确且众所周知。

当然,真正的解决方案是使用TypeScript等打字系统。它确实提供私人会员/方法。如果方法确实需要公开,TS将不允许您在子类上使用相同的名称定义方法,除非签名匹配。

答案 2 :(得分:0)

最小化这一点的一种可能方法,就是我能想到的最好的方法,就是包裹对象的内部状态,例如:

class Evented {
    constructor(parent) {
        this.eventedState = {
           parent : parent
        }

    }
}

并将相同的模式应用于所有类。显然,这仍然意味着每个对象可能会碰撞一个属性,但它会减少碰撞的可能性。这样做的缺点是它不完美,重构代码以使用这种模式可能会非常痛苦。