2016-12-15 5 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

だろうあなたの例のために、あなたのオブジェクトの内部状態をラップしている、私は私の頭の右上考えることができ、これを最小化する1つの可能な方法:

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

    } 
} 

を及びそれを適用しますパターンをすべてのクラスに適用します。明らかに、これは依然としてオブジェクトごとに1つのプロパティが衝突する可能性があることを意味しますが、衝突の可能性は減ります。これの欠点は、完全ではなく、このパターンを使用するようにコードをリファクタリングすることはかなり苦しいことかもしれないということです。

2

大規模なES6 JavaScriptアプリケーションは、多くの継承を使用します。

実際、ほとんどの人はそうではありません。 Angularのようなフレームワークでは、プラットフォーム定義のクラスからの継承のレベルが最も一般的です。深い継承構造は脆く、避けるのが最善です。大規模なアプリケーションでは、A> Bという2つのユーザーレベルのクラスのケースがあります。ここで、Aには一連の共通ロジックが含まれ、B1とB2は、コンポーネントの別のテンプレートなどの軽い特殊化です衝突に関する懸念が高まっています。

Eventedの例は、意味的には実際には親クラスではありません。それはミックスインの本質のほうがはるかです。あなたが本当にミックスインのようなスーパークラスとして使用するためEventedを設計する場合

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

:JSは本当に代わりにEventedからTreeを導出する、うまくミックスインを処理していないので、私はプロパティとしてイベント化オブジェクトを保持したいです別の回答で提案されているように、それは、

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との最近の衝突は、内部のEmberプロパティと衝突するようになった(WebGLレンダラを保持する)コンポーネントのrendererプロパティを持っていました(私はそれが私には見えませんドキュメント内)。私は 'send'と' sendAction'で同様の問題を抱えていましたが、どちらも十分に文書化されています。二度は私のためにあまりにも多くです。 – Coburn

関連する問題