如何避免 ES6 Javascript class 继承命名冲突
How to avoid ES6 Javascript class inheritance naming collisions
如何避免 ES6 Javascript 中 class 继承的命名冲突?
大型 ES6 Javascript 应用程序使用大量继承,以至于在基础 class 中使用通用名称可能意味着稍后在创建派生 classes 时会出现令人头疼的调试问题。这可能是 class 设计不佳的产物,但似乎更多是 Java 脚本能够顺利扩展的问题。其他语言提供了隐藏继承变量 (Java) 或属性 (C#) 的机制。缓解此问题的另一种方法是使用 Javascript 没有的私有变量。
下面是此类碰撞的示例。一个 TreeObject class 扩展了一个事件对象(以继承事件功能)但它们都使用 parent
来存储它们的父对象。
class Evented {
constructor(parent) {
this.parent = parent;
}
}
class TreeObject extends Evented{
constructor(eventParent, treeParent) {
super(eventParent);
this.parent = treeParent;
}
}
虽然这个例子有点人为,但我在像 Ember 这样的大型库中遇到过类似的冲突,其中库和最终应用程序之间的术语有很多重叠,导致我到处浪费时间。
这似乎确实是一个设计问题(使用较小的对象和更扁平的层次结构),但也有解决您的问题的方法:symbols!
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;
}
}
它们将可靠地防止任何冲突,因为所有符号都是唯一的,无论它们的描述符如何。
我能想到的最小化这种情况的一种可能方法是包装对象的内部状态,例如:
class Evented {
constructor(parent) {
this.eventedState = {
parent : parent
}
}
}
并对所有 类 应用相同的模式。显然这仍然意味着每个对象有一个 属性 可能发生碰撞,但它减少了发生碰撞的机会。这样做的缺点是它并不完美,并且重构您的代码以使用此模式可能会非常痛苦。
Large ES6 JavaScript applications use a lot of inheritance.
实际上,他们中的大多数人都没有。在像 Angular 这样的框架中,平台定义的 classes 的一级继承是最常见的。深度继承结构很脆弱,最好避免。一个大型应用程序可能有两个用户级别的情况 classes,A > B,其中 A 包含一堆通用逻辑,B1 和 B2 是轻型专业化,例如组件的不同模板,在不会引起碰撞担忧的水平。
您的 Evented
示例在语义上并不是真正的父 class;它更像是mixin的本质。由于 JS 并不能很好地处理 mixin,我不会从 Evented
派生 Tree
,而是将事件对象保存为 属性:
class TreeObject {
constructor(eventParent, treeParent) {
this.evented = new Evented(eventParent);
this.parent = treeParent;
}
send(msg) { this.evented.send(msg); }
}
如果你真的想把Evented
设计成类似mixin的superclass,那么设计者有责任让成员变量尽可能唯一,如
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);
}
}
I've had similar collisions in large libraries like Ember
我没有。 Ember classes 通常定义很少的成员,并且它们定义的方法定义明确且众所周知。
当然,真正的解决方案是使用打字系统,例如 TypeScript。它确实提供私有 members/methods。如果一个方法确实需要 public,TS 不会让你在 subclass 上定义一个同名的方法,除非签名匹配。
如何避免 ES6 Javascript 中 class 继承的命名冲突?
大型 ES6 Javascript 应用程序使用大量继承,以至于在基础 class 中使用通用名称可能意味着稍后在创建派生 classes 时会出现令人头疼的调试问题。这可能是 class 设计不佳的产物,但似乎更多是 Java 脚本能够顺利扩展的问题。其他语言提供了隐藏继承变量 (Java) 或属性 (C#) 的机制。缓解此问题的另一种方法是使用 Javascript 没有的私有变量。
下面是此类碰撞的示例。一个 TreeObject class 扩展了一个事件对象(以继承事件功能)但它们都使用 parent
来存储它们的父对象。
class Evented {
constructor(parent) {
this.parent = parent;
}
}
class TreeObject extends Evented{
constructor(eventParent, treeParent) {
super(eventParent);
this.parent = treeParent;
}
}
虽然这个例子有点人为,但我在像 Ember 这样的大型库中遇到过类似的冲突,其中库和最终应用程序之间的术语有很多重叠,导致我到处浪费时间。
这似乎确实是一个设计问题(使用较小的对象和更扁平的层次结构),但也有解决您的问题的方法:symbols!
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;
}
}
它们将可靠地防止任何冲突,因为所有符号都是唯一的,无论它们的描述符如何。
我能想到的最小化这种情况的一种可能方法是包装对象的内部状态,例如:
class Evented {
constructor(parent) {
this.eventedState = {
parent : parent
}
}
}
并对所有 类 应用相同的模式。显然这仍然意味着每个对象有一个 属性 可能发生碰撞,但它减少了发生碰撞的机会。这样做的缺点是它并不完美,并且重构您的代码以使用此模式可能会非常痛苦。
Large ES6 JavaScript applications use a lot of inheritance.
实际上,他们中的大多数人都没有。在像 Angular 这样的框架中,平台定义的 classes 的一级继承是最常见的。深度继承结构很脆弱,最好避免。一个大型应用程序可能有两个用户级别的情况 classes,A > B,其中 A 包含一堆通用逻辑,B1 和 B2 是轻型专业化,例如组件的不同模板,在不会引起碰撞担忧的水平。
您的 Evented
示例在语义上并不是真正的父 class;它更像是mixin的本质。由于 JS 并不能很好地处理 mixin,我不会从 Evented
派生 Tree
,而是将事件对象保存为 属性:
class TreeObject {
constructor(eventParent, treeParent) {
this.evented = new Evented(eventParent);
this.parent = treeParent;
}
send(msg) { this.evented.send(msg); }
}
如果你真的想把Evented
设计成类似mixin的superclass,那么设计者有责任让成员变量尽可能唯一,如
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);
}
}
I've had similar collisions in large libraries like Ember
我没有。 Ember classes 通常定义很少的成员,并且它们定义的方法定义明确且众所周知。
当然,真正的解决方案是使用打字系统,例如 TypeScript。它确实提供私有 members/methods。如果一个方法确实需要 public,TS 不会让你在 subclass 上定义一个同名的方法,除非签名匹配。