Object.mixin() reacher proposal

# Andrea Giammarchi (12 years ago)

what I've written here: WebReflection/object-mixin/blob/master/src/object-mixin.js

is a better proposal for the potential Object.mixin() in current ES6 specs.

It seems that Mixins Are Awesome and this can take most advantages from being a function and not only an object: webreflection.blogspot.ie/2013/04/flight-mixins-are-awesome.html

AFAIK, all interfaces described in W3C such EventTarget and others could be also covered by this proposal ... so ... what do you think ?

/*jslint browser: true, forin: true, plusplus: true, indent: 4 */ (function(Object, mixin) { "use strict"; // happy linter ^_____^

/* <droppable>
 * adhoc polyfill section for this purpose only
 * never use these functions outside this closure ... like ...

ne*/var /* ^ ... you see that? only reason I chose 4 spaces indentations here :D also this comment ... pure quality, right ?!?! ... anyway ... */

    // for IE < 9 Desktop browsers
    defineProperty = Object.defineProperty ||
    function (o, k, d) {
        o[k] = d.value;
    },
    // same as above
    getOwnPropertyNames = Object.getOwnPropertyNames ||
    function (o) {
        var
            // in case the guy does not inherit from Object.prototype
            has = Object.prototype.hasOwnProperty,
            result = [],
            key;
        for (key in o) {
            // in non ES5 compliant browsers
            // there's no way to define properties
            // as non enumerable unless these are
            // there by default, like "constructor" is
            // for functions.prototype
            if (has.call(o, key)) {
                result.push(key);
            }
        }
        return result;
    },
    // again ... IE < 8
    getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor ||
    function (o, k) {
        return {
            enumerable: true,
            writable: true,
            configurable: true,
            value: o[k]
        };
    };
// </droppable>

// if already defined get out of here
// this should be
// if (mixin in Object) return;
// but for some reason I went for JSLint ...
if (Object[mixin]) {
    return;
}
// same descriptor as other spec'd methods
defineProperty(
    Object,
    mixin,
    {
        enumerable: false,
        writable: true,
        configurable: true,
        value: function mixin(
            target, // object to enrich with
            source    // mixin object/function
        ) {
            var
                // check if source is a function
                enricher = typeof source === 'function' ?

source.prototype : source, // per each own property name keys = getOwnPropertyNames(enricher), length = keys.length, i = 0, key; while (i < length) { // define it ... defineProperty( target, key = keys[i++], // ... via same property descriptor getOwnPropertyDescriptor( source, key ) ); } // if the object had no own names // it's quite clear the intention of the user // so that if a function without properties // is passed through this method ... if (!length && typeof source === 'function') { // this function is invoked with the target // as its own context source.apply( target, // optional arguments to initialize defaults // for this mixin might be accepted too keys.slice.call(arguments, 2) ); } // always return the initial target // ignoring a possible different return // in latter case: consistency with this method return target; } } ); }(Object, 'mixin'));

# Andrea Giammarchi (12 years ago)

apologies getOwnPropertyDescriptor( source, key )

should have been getOwnPropertyDescriptor( enricher, key )

# Andrea Giammarchi (12 years ago)

also, in case you are guessing the typo .. reacher because it could reach more (older) engines, doing a joke with richer .... got it? .. too damn fun, I know!

# Andrea Giammarchi (12 years ago)

OK, maybe just code was a non-sense ...

So, the idea behind is mark a function explicitly as mixin ... how ?

Any function that is passed and has an empty prototype (then is user defined or native) could be considered invocable as mixin.

function addFunctionality() {
  this.method = function () {
    // now the outer context has a method
  };
}

// mark the prototype as empty in ES5
delete addFunctionality.prototype.constructor;

function MyClass() {}

Object.mixin(MyClass.prototype, addFunctionality);

rather than only

Object.mixin(MyClass.prototype, {method: function () {}});

If the prototype has at least one own property in its prototype it will be considered a constructor so that:

Object.mixin(MyClass.prototype, MySuperClass);

can easily be transformed implicitly into:

Object.mixin(MyClass.prototype, MySuperClass.prototype);

This case is, however, less important, the fact Object.mixin should be able to accept a function and invoke it with target as context with optional arguments would be really a great idea, IMHO

# Andrea Giammarchi (12 years ago)

right, I've simplified a lot and tested cross platform:

WebReflection/object-mixin#object-mixin

thoughts?

# Angus Croll (12 years ago)

Lending my support to Object.mixin accepting a function as the argument?but no surprise there I guess :)

Note: since functional mixins and constructors are syntactically identical we can now get gorgeously expressive?and make type inheritance way simpler (for the first time allowing multiple type inheritance)

//make a new thing and a new thang
var thing = new Thing;
var thang = new Thang;

//OR have Thung inherit from Thing and Thang
Object.mixin(Thung.prototype, Thing);
Object.mixin(Thung.prototype, Thang);
# Andrea Giammarchi (12 years ago)

My previous version was doing that in a probably too smart way so I've simplified the proposal simply accepting, in that example

Object.mixin(Thung.prototype, Thing.proottype); Object.mixin(Thung.prototype, Thang.proottype);

It does not look so black magic anymore but it's way less ambiguous than the first proposal (I guess)

# Angus Croll (12 years ago)

yeah that's better - I was having a senior moment - most constructor functions will normally reside in the prototype of course

# Andrea Giammarchi (12 years ago)

somebody already raised the concern "what if I want to mixin the function as object, not as callable"

I think being [[Call]] not possible to mixin as object functionality, and being functions all by default having ownProperties such ["arguments", "name", "length", "caller"] .. that would simply clash so function as argument, for this purpose, is never ambiguous, but of course a function could be the target object, if needed

var withMoar = Object.mixin(function(){}, mixinFunction); // same as mixinFunction.call(function(){});

# Peter Seliger (12 years ago)

Hi, maybe one should discuss terminology first.

What has been rediscovered within the last decade every now and again by JavaScript programming individuals, this languages capability for "functional or function based mixins", might deserve a closer look, before running into what I would call a meta programming temptation trap.

We already have everything we need in order to create modular collections of behavior and also in order to provide and apply them to objects.

"Mixin" might be a Ruby influenced term and does not completely cover what can be achieved by functions/closures [call] and [apply]. I'd rather tend to use Perl 6 "Roles" as a generic term.

The smallest thinkable Role was a function body that implements a single method in a stateless way. Thus being a "Trait" if one follows the findings of the "Software Composition Group" at Bern University [ scg.unibe.ch/research/traits]. Any implementation that gets injected mutable state or does create mutable state on its own in oder to solve its task(s) then, from my point of view, should be referred to as "Mixin".

It seems that Mixins Are Awesome and this can take most advantages from being a function and not only an object: webreflection.blogspot.ie/2013/04/flight-mixins-are-awesome.html

AFAIK, all interfaces described in W3C such EventTarget and others could be also covered by this proposal ... so ... what do you think ?

Andrea, you are right, but all it needs in my opinion are a module pattern, a module library of your choice, a naming convention for your Trait/Mixin-module Implementation (adjectives/adverbes, no nouns, first uppercase letter?) and again [call] or [apply].

For your given example of W3C interfaces, [EventTarget] should be implemented and internally referred to as [EventTargetMixin], but the module should expose [EventTargetMixin] as [Observable].

There is another point that makes pure straightforward functional Trait/Mixin composition unique in JavaScript - passing additional data at apply time. One could write e.g. implementations that at apply time can adapt the naming of their functional API without changing the underlying implementation itself. [Observable] from the above provided gist example enables configuration of the default API method names by an optionally passed config object making it possible to serve all those weird observable API dialects.

var obj_01 = {};
var obj_02 = {};

Observable.call(obj_01); /* does add the default api methods [addEventListener], [removeEventListener], [hasEventListener] and [dispatchEvent] */

Observable.call(obj_02, {
  addEventListener: "on",
  removeEventListener: "off",
  hasEventListener: "hasObserver",
  dispatchEvent: "trigger"
}); /* obj_02 does feature the custom api names [on], [off], [hasObserver] and [trigger] - the underlying methods are the same as of obj_01 */

With meta programming approaches this advantage, one gets for free now, might get lost or if implemented less understandable.

I discarded every single approach I made within the last 5 years for Trait/Mixin libraries, that tried to be smarter than what the languages core already provides except the last one that just sticks to a module library.

# Andrea Giammarchi (12 years ago)

not sure changing name is a thing to promote, it makes method name clashing easier but it can be achieved simply attaching properties to a specific object and then pass that as mixin ...

Object.mixin(target, {
  on: source.addEventListener,
  off: source.removeEventListener,
  has: source.hasEventListener,
  trigger: source.dispatchEvent
});

As you see there's no need to make the signature more complex with aliases, it's straight forward to do the same, of course if those descriptor are meant to be (enumer|configur|writ)able: true)

Agreed on Trait naming convention, but these can be represented only via Functions in JS.

However, describing them as "not exactly common function" would result in a better understanding.

So here the API, and the name I am afraid has always been abused in JS but is known as "mixin"

// basic signature
Object.mixin(
  target:Object,
  source:Object
):target

// one overload
Object.mixin(
  target:Object,
  source:Trait
  [,args:Array|Arguments]
):target

This is probably even better than needed/horrible slice call and arguments.length check per each mixin invocation with Traits ... so, what do you think ?

# Andrea Giammarchi (12 years ago)

updated API with Trait and the extra argument: WebReflection/object-mixin#api

how does the code look like now: WebReflection/object-mixin/blob/master/src/object-mixin.js

is your browser passing all tests? (if green, yes) webreflection.github.io/object-mixin/test

Best