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

如何避免ES6 Javascript类继承命名冲突,javascript,inheritance,ecmascript-6,es6-class,Javascript,Inheritance,Ecmascript 6,Es6 Class,如何避免ES6JavaScript中类继承的命名冲突 大型ES6JavaScript应用程序使用大量继承,以至于在基类中使用泛型名称可能意味着在以后创建派生类时调试困难。这可能是糟糕的类设计的产物,但更可能是Javascript能够顺利扩展的问题。其他语言提供了隐藏继承变量(Java)或属性(C#)的机制。另一种缓解这个问题的方法是使用私有变量,而Javascript没有私有变量 下面是这样一个碰撞的例子。TreeObject类扩展事件对象(以继承事件功能),但它们都使用parent存储它们的

如何避免ES6JavaScript中类继承的命名冲突

大型ES6JavaScript应用程序使用大量继承,以至于在基类中使用泛型名称可能意味着在以后创建派生类时调试困难。这可能是糟糕的类设计的产物,但更可能是Javascript能够顺利扩展的问题。其他语言提供了隐藏继承变量(Java)或属性(C#)的机制。另一种缓解这个问题的方法是使用私有变量,而Javascript没有私有变量


下面是这样一个碰撞的例子。TreeObject类扩展事件对象(以继承事件功能),但它们都使用
parent
存储它们的父对象

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

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

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

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


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

一种可能的最小化方法,我可以从头顶上想到,就是包装对象的内部状态,例如:

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

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

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

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

您的
事件化的示例在语义上不是真正的父类;这更像是一个混血儿。由于JS不能很好地处理混合,因此我将事件对象作为一个属性保存,而不是从
Evented
派生
Tree

class TreeObject {
    constructor(eventParent, treeParent) {
        this.evented = new Evented(eventParent);
        this.parent = treeParent;
    }
    send(msg) { this.evented.send(msg); }
}
如果您真的想设计
events
作为类似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将不允许您在子类上定义同名的方法,除非签名匹配。

这是这个问题唯一客观的答案。如果所讨论的符号是文件的本地符号,我甚至可以认为这是对私有变量的一个很好的模拟。@Coburn不完全是,每个人仍然可以枚举任意对象的符号键属性,就像使用字符串键一样。只需将
对象.getOwnPropertyNames
更改为
对象.getOwnPropertySymbols
@Bergi-Dang,这仍然是一个整洁的功能。在Ember冲突中,我最近与Ember的冲突是在组件上有一个
渲染器
属性(用于保存WebGL渲染器)这最终与一些内部灰烬财产发生了冲突(我认为这是私人的,我在文档中没有看到)。我对
send
sendAction
也有类似的问题,但这两个问题都有很好的文档记录。两次对我来说太多了。
const parents = new Map();

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