javascriptnaming-conventionscompositionobject-design

How to avoid name clashes when composing objects


In JavaScript you can compose objects using some kind of extend function.

For example I might have an observable class that exposes a set of public methods (get, push, set, increment, get, etc)

In this case the observable also happens to be an EventEmitter so it also exposes a further set of public methods (emit, on, removeListener, etc)

Both of these classes have internal underscore prefixed properties that store the state. The eventemitter uses _events to store event handlers and the observable uses _state and _id to store state and the id.

Now when I create a model using object composition like so

var Model = extend({}, Observable, {
    constructor: function () {
        // Oops, I was supposed to know Observable uses the _state name already
        this._state = { ... }
    },
    someMethod: function () { ... }
})

This causes an issue because Observable already uses the _state internal property and there is now a name clash.

I would consider it ugly to just "have to know" what objects rely on what internal properties for the mixin to safely work.

How do you avoid mixing in two objects which use the same internal property name?

Ideally this would be solved with ES6 private names, but we can't do that yet and we can't emulate them without losing performance. Unless you can provide a ES6 name emulation that does not have a large performance penalty I'm not interested in those solutions.

An alternative would be to use closures or bind but then you would be recreating the functions for every instance with is a significant performance penalty. Another alternative would be to namespace internal properties as __eventEmitter_events and __observable_state. That is just ugly and reduces to probability of a namespace clash, it doesn't remove it.


Solution

  • The trivial solution is "namespaces"

    var state = "Model@v0.1.3~state";
    var Model = extend({}, Observable, {
        constructor: function () {
            // Nice, I used a namespace and don't clash with "Observable@v0.2.3~state"
            this[state] = { ... }
        },
        someMethod: function () { ... }
    })