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

В больших приложениях JavaScript ES6 используется много наследования.

Фактически, большинство из них этого не делают. В такой структуре, как Angular, один уровень наследования от определенных платформой классов наиболее распространен. Глубокие структуры наследования хрупки и лучше всего избегают. Большое приложение может иметь несколько случаев с двумя уровнями пользователей, A> B, где A содержит кучу общей логики, а B1 и B2 - это легкие специализации, такие как другой шаблон для компонента, на уровне, который не дает возникают опасения по поводу столкновений.

Ваш пример Evented семантически не является родительским классом; это больше похоже на микс.Так как JS не очень хорошо обрабатывать Примеси, вместо получения Tree из Evented, я бы держать evented объект как свойство:

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 (я думаю, что это частный bc, я не вижу его в документах). У меня была аналогичная проблема с 'send' и' sendAction', но оба документа хорошо документированы. Дважды слишком много для меня. – Coburn