2
\$\begingroup\$

I've read a lot of stuff about mixins, inheritance and such, and in the end I came up with this solution for extending a class with multiple mixins. I haven't seen this anywhere else...

Is this a good method? Do you see any problems with it?

What I'm trying to achieve:

I need classes that share properties and methods, and extend methods.

The code is used in two separate projects: a viewer and an editor. The editor adds code on top of the viewer's to add functionality to the classes.

Ex: img class is a base object and a positional object. It has a setAttribute method which also calls base.setAttribute and pos.setAttribute. "page" class is a base object (but not a position object) and so on. With an additional JavaScript, these objects are expanded further for editing capabilities.

This is a part of the actual code (viewer):

var Objects = {};
Objects.extend = function () {
    for (var i = 1; i < arguments.length; i++) {
        Objects[arguments[i]].call(Objects[arguments[0]].prototype);
    }
}

// mixin base object
Objects.__base = function () {
    this.init = function (e, obj) {
        this.e = e;
        this.visible = true;
        this.type = "";

        this.container = null;

        this.attributes = {};
        this.obj = obj; // dictionary object
    }

    // check absolute visibility, containers...
    this.isVisible = function () {
        return (this.visible && (!this.container || this.container.isVisible()));
    }

    this.getAttribute = function (name) {
        return this.attributes[name];
    }

    this.updateAttribute = function (name, value) {
        if (value != null) {
            this.attributes[name] = value;
            return true;
        } else {
            return false;
        }
    }

    this.setAttribute = function (name, value) {
        var v;
        // general attributes (for every kind of object)
        switch (name) {
        case "id":
            v = value;
            this.e.setAttribute("data-id",v);
            break;
        case "visible":
            v = parseBool(value != "false" && value != false);
            // show/hide?
            if (this.visible != v) {
                this.e.style.display = v?"inline":"none";
                this.visible = v;
            }
            break;
        } // switch general attributes

        return this.updateAttribute(name, v);
    } // setAttribute()
} // Objects.__base mixin

// mixin base positional object
Objects.__pos = function () {
    var _setAttribute = this.setAttribute;
    this.setAttribute = function (name, value) {
        var v;
        switch (name) {
        case "x":
        case "y":
            v = parseInt(value);
            this.e.style[name=="x"?"left":"top"] = v+"px";
            break;
        default:
            return _setAttribute.call(this, name, value);
        } // switch general attributes

        return this.updateAttribute(name, v);
    } // setAttribute()
} // Objects.__pos mixin

// image class
Objects.image = function () {
    // set attribute
    var _setAttribute = this.setAttribute;
    this.setAttribute = function (name, value) {
        var v;
        // general attributes (for every kind of object)
        switch (name) {
        case "width":
        case "height":
            v = parseInt(value);
            var img = this.e.getElementsByTagName("img")[0];
            img.style[name] = v+"px";
            break;
        default:
            return _setAttribute.call(this, name, value);
        }

        return this.updateAttribute(name, v);
    } // setAttribute()
} // Objects.image class

Objects.extend("image","__base","__pos");

And then, in the additional editor JavaScript, I extend all of the objects to add editing capabilities that I don't need on the viewer:

Objects.__editorObj = function () {
    var _init = this.init;
    this.init = function (e, obj) {
        if (_init) _init.call(this, e, obj);
        // absolute coordinates (relative to the page), set by openContainer
        this.ax = 0;
        this.ay = 0;
    }
    this.move = function (dx, dy) {
        // ...
    }
}

for (var o in Objects) // extend all the objects with editing capabilities
    if (o.charAt(0)!="_") 
        Objects.extend(o,"__editorObj");

I'd also like to have private attributes and methods in the mixins, but I don't know how. I tried to use mixins because I read that trying to use JavaScript as a class based language is not a good practice.

\$\endgroup\$
  • \$\begingroup\$ I wrote a js framework that has a class system that supports multiple mixins and even compositions. Check it out, it might work for your situation github.com/pllee/luc \$\endgroup\$ – pllee Jun 10 '14 at 14:22
1
\$\begingroup\$

Trying to use JavaScript like a class based language is what you make it. It sounds like someone was expressing an opinion, rather than imperial evidence.

As for your implementation, it's very confusing how you'll expect to use the Objects object. Be very careful with mixins or, "multiple inheritance." If you need multiple inheritance, I would argue that you need Composition over inheritance.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.