繁体   English   中英

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

[英]How to avoid ES6 Javascript class inheritance naming collisions

如何避免在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这样的大型库中遇到了类似的冲突,其中库和最终应用程序之间的术语重叠了很多,导致我浪费时间在这里和那里浪费。

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

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;
    }
}

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

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

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

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

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将不允许您在子类上使用相同名称定义方法,除非签名匹配。

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

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

    }
}

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM