3

I'd like to use Object.assign to "upgrade" an object with new methods temporarily, and then remove those methods when I'm done using them. An example will clarify:

Say we have a mixin that allows us to calculate the average of an array:

var ArrayUtilMixin = {
  avg() {
    let sum = this.reduce( (prev, v) => {return prev + v}, 0);
    return sum / this.length;
  }
};

Our client code uses this like so:

let myArr = [0,3,2,4,88];
// now I am in a context where I want to average this array, 
// so I dynamically add the ability with Object.assign
Object.assign(myArr, ArrayUtilMixin);
let avg = myArr.avg();
// do some stuff here with the average
// now we're done, we want declutter the myArr object
// and remove the no longer needed avg() method

Object.unassign(myArr, ArrayUtilMixin);  // <-- CAN WE DO THIS SOMEHOW?

Is there any way to accomplish this? If not, am I using the wrong language feature for what I really want -- that ability to dynamically add and remove object methods at runtime, depending on context.

Jonah
  • 15,806
  • 22
  • 87
  • 161
  • 3
    You could simply iterate over the properties of `ArrayUtilMixin` and delete them from `myArr`. That only works if assigning `ArrayUtilMixin` doesn't *overwrite* any properties of `myArr`. Another interesting approach would be to plug `ArrayUtilMixin` into the prototype chain of `myArr`. But that might not work properly with native objects. – Felix Kling Apr 06 '15 at 06:06

1 Answers1

2

Is there any way to accomplish this?

There are some, but I think none of them does exactly what you want to do:

  • use Object.assign, then afterwards delete the new properties

    Object.unassign = function(o, mixin) {
        for (var p in mixin)
            delete o[p]; // deletes own properties only, so don't fear
        return o;
    }
    

    This doesn't work well when you have overwritten own methods/properties of course.

  • alter the prototype chain of the object you want to extend

    function extend(o, mixin) {
        var m = Object.assign({}, mixin);
        Object.setPrototypeOf(m, Object.getPrototypeOf(o));
        Object.setPrototypeOf(o, m);
        return o;
    }
    function unextend(o) {
        Object.setPrototypeOf(o, Object.getPrototypeOf(Object.getPrototypeOf(o)));
        return o;
    }
    

    The advantage of this approach is that own properties stay own properties, so assignments on the object will work as usual. There are some languages that endorse this pattern (and combine it with multiple inheritance), but I'm not sure how well it really works. Of course, modifying the prototype chain is a really bad idea in JavaScript.

  • prepend to the prototype chain

    function extended(o, mixin) {
        return Object.assign(Object.create(o), mixin);
    }
    

    This creates a new object with the mixin methods that inherits from the actual object. You'd "unextend" by just throwing away the temporary one, and use the old again (not exactly the usage pattern you had in mind I guess?) - you can hide this fact by storing the old one in a property and "unwrap" with a unextend() function.

    Of course, the drawback of this otherwise simple and efficient pattern is that assignments to the temporary object don't work. They would create new, own properties instead of modifying the actual object, and would get thrown away once you "unextend". This doesn't matter for your avg method, and can even be utilised for some mixins, but you might not want this.

If not, am I using the wrong language feature

It's quite possible that there is no language feature for this.

The most common advice for cases like this is to construct a wrapper object (e.g. around DOM objects), which acts as a proxy between the user and the actual object. The API of the wrapper is completely different from the wrapped object's one though; this is not a simple "extension".

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • "Of course, modifying the prototype chain is a really bad idea in JavaScript." -- why is this bad? specifically in the example you gave? thanks. – Jonah Dec 21 '15 at 08:27
  • @Jonah: See [Why is mutating the \[\[prototype\]\] of an object bad for performance?](http://stackoverflow.com/q/23807805/1048572) – Bergi Dec 21 '15 at 09:21
  • "There are some languages that endorse this pattern (and combine it with multiple inheritance)" -- what are the best examples of these? – Jonah Jan 26 '16 at 14:02
  • @Jonah: [Io](https://en.wikipedia.org/wiki/Io_(programming_language)) does it, and I think either Self or NewtonScript as well. – Bergi Jan 26 '16 at 16:01