2016-12-15 112 views
3

如何避免命名ES6 Javascript中的類繼承衝突?如何避免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; 
    } 
} 

雖然這個例子是有點做作,我已經在像灰燼大型圖書館類似的衝突在圖書館和最終應用之間的術語相當多的重疊導致我在這裏和那裏浪費時間。

回答

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

他們將可靠地防止任何衝突,因爲所有的符號都是獨一無二的,不論其描述符。

+3

此問題的唯一*目標*答案。 –

+0

我甚至可以看到這是一個很好的私有變量仿真,如果有問題的符號是文件本地的。 – Coburn

+0

@Coburn不完全是,每個人都可以枚舉任意對象的符號鍵屬性,就像使用字符串鍵的屬性一樣。這只是將Object.getOwnPropertyNames改爲Object.getOwnPropertySymbols的問題。 – Bergi

0

儘量減少這一點,我能想到我的頭右頂部,被包裝的對象的內部狀態,爲您的示例的一種可能的方式是:

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

    } 
} 

並應用相同模式到所有的類。顯然這仍然意味着每個對象有一個可能會碰撞的屬性,但它會減少碰撞的可能性。這樣做的缺點是它並不完美,並且重構你的代碼來使用這種模式可能會非常痛苦。

2

大型ES6 JavaScript應用程序使用了大量的繼承。

其實大部分都沒有。在像Angular這樣的框架中,平臺定義的類的一種繼承是最常見的。深層的繼承結構是脆弱的,並且最好避免。一個大型的應用程序可能有兩個用戶級別的類A> B,其中A包含一堆公共邏輯,而B1和B2是輕型特化,比如組件的不同模板,其級別不會給引起對碰撞的擔憂。

Evented的例子在語義上並不是真正的父類;它更多是混合的本質。由於JS並沒有真正處理好混入,而不是從Evented獲得Tree,我會保持事件觸發對象作爲一個屬性:

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

如果你真的想設計Evented用作混入般的超,那麼它的設計使成員變量儘可能獨特的責任,如

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類通常定義了很少的成員,他們定義的方法是明確定義的並且是衆所周知的。

當然,真正的解決方案是使用打字系統,如TypeScript。它確實提供私人成員/方法。如果方法確實需要公開,除非簽名匹配,否則TS不會讓您在子類上使用相同的名稱定義方法。

+0

在Ember碰撞中,我最近與Ember的碰撞是在組件上有一個'renderer'屬性(用於存放一個WebGL渲染器),最終與一些內部的Ember屬性相沖突(我認爲它是私有的,但我看不到它在文檔中)。我有'send'和'sendAction'類似的問題,但這兩個都有很好的文檔。兩次對我來說太過分了。 – Coburn