Blog Archive

2014-04-11

The-many-Talents-of-JavaScript

The many »Talents« of JavaScript

The many talents of JavaScript for generalizing Role Oriented Programming approaches like Traits and Mixins

TL;DR / Summary

  • Role-oriented programming is a structural programming paradigm targeting separation of concerns in object oriented programming.
  • »Mixin« and »Trait« are established terms and core features in programmig languages; both do meet the Role paradigm.
  • Mixin and Trait based composition predominantly targets a language’s class level.
  • Traits do provide composition operations in order to resolve conflicts, thus theirs composition precedence is not as relevant as with Mixins that only support linear order composition.
  • Behavior as provided with Mixins and Traits can not be aquired or lost at runtime.
  • As for Traits, for quite a while there was no real guideline for how to handle state.
  • Talents are an academical concept that enhances Role approaches like Trait and Mixin, for it does develop further some of theirs features but also harmonizes theirs characteristics and eases theirs usage in practice.
  • JavaScript easily adpots to Talents because of its core language concepts like »Closure« and »Delegation«.

Implicit and Explicit Delegation

JavaScript1 is a Delegation Language.

Functions as Roles (Traits and Mixins)

JavaScript natively supports2 3 4 5 various function based implementations of Role approaches like Traits and Mixins. Such a function defines additional behavior by at least one method bound to the this6 keyword within its function7 body. A Role then has to be delegated explicitly via call8 or apply9 to objects that are in need of additional behavior that is not shared via the prototype chain10.

var Enumerable_first = function () {
  this.first = function () {
    return this[0];
  };
};
var list = ["foo", "bar", "baz"];

console.log("(typeof list.first)", (typeof list.first)); // "undefined"

Enumerable_first.call(list); // explicit delegation

console.log("list.first()", list.first()); // "foo"

Object Composition and Inheritance

Whereas explicit function based delegation does cover object composition in JavaScript, implicit delegation already happens every time the prototype chain is walked in order to e.g. find a method that might be related to but is not directly owned by an object. Once the method was found, it gets called within this objects context. Thus inheritance in JavaScript is covered by a delegation automatism that is bound to the prototype slot of constructor functions.

var Enumerable_first_last = function () {
  this.first = function () {
    return this[0];
  };
  this.last = function () {
    return this[this.length - 1];
  };
};
console.log("(typeof list.first)", (typeof list.first));  // "function"   // as expected
console.log("(typeof list.last)", (typeof list.last));    // "undefined"  // of course

Enumerable_first_last.call(Array.prototype);  // applying behavior to [Array.prototype]

console.log("list.last()", list.last());      // "baz"  // due to delegation automatism

»Mixins«

According to the English version of Wikipedia Flavors, …

… an early object-oriented extension to Lisp
, was the first programming language to include mixins.

Thus introducing code reuse across class hierarchies. Summing it up very briefly; even though Mixin approaches have been evolved over time, theirs composition mostly is limited to classes (Ruby does allow composition at instance level). And due to the lack of composition operators that otherwise support conflict resolution of rivaling (identically named) methods, Mixins need to be composed in linear order.

»Traits«

Traits11 have been extensively researched and developed within a decade starting as far back as November 200212 at the »Software Composition Group«13 (SCG), University of Bern.

The concept of Traits overcomes the aforementioned limitations of composition with Mixins, making the latter more flexible since Traits provide composition operators for combination, exclusion or aliasing of methods. And because Traits in one of theirs later development cycle (2006) are allowed to be stateful14 (the earliest SCG Traits were not allowed carrying state), JavaScript’s language features and its pure function based adoption of the Traits concept were meant for each other at least for the last couple of years.

Being a classless, almost entirely object based, in many ways highly dynamic language, capable of a 2 way delegation and acting out its at least partly functional nature, JavaScript can reach beyond the bounderies of Traits as well. The concept of the latter e.g. does not support behavioral changes of objects at runtime. Even though Trait composition is very flexible its mechanics target classes only. Traits also can not be added to or removed from classes at runtime.

But there is a not anymore that new kid on the block15 facing JavaScript at eye level. Now it is JavaScript’s turn to playfully explore it and also to evolve growth from a discovering journey that just has started …

»Talents: Dynamically Composable Units of Reuse«

The identically named SCG paper from July 2011 introduces Talents16 as …

… object-specific units of reuse which model features that an object can acquire at run-time. Like a trait, a talent represents a set of methods that constitute part of the behavior of an object. Unlike traits, talents can be acquired (or lost) dynamically. When a talent is applied to an object, no other instance of the object’s class are affected. Talents may be composed of other talents, however, as with traits, the composition order is irrelevant. Conflicts must be explicitly resolved.

Like traits, talents can be flattened, either by incorporating the talent into an existing class, or by introducing a new class with the new methods. However, flattening is purely static and results in the loss of the dynamic description of the talent on the object. Flattening is not mandatory, on the contrary, it is just a convenience feature which shows how traits are a subset of talents.

This pretty much sounds like a perfect match; as though the concept of Talents was made just in order to meet JavaScript’s language design and to fit its capabilities in means of implementing Talents in this languages most natural way. Composition can be achieved in both ways, at object (instance) and constructor (class) level. And Talents acknowledge state.

From »Mixins« to »Traits« - the JavaScript mechanics behind pure function based »Talents«

Talents by theirs scientific research results are supposed to work on both instance level and class level. Talents might be used as straightforward as Mixins but also can provide additional functionality that covers composition operators in order to be used like Traits.

The »Mixin Approach«

The most fine grained Talent implements a sole method without introducing state and without conflict resolution. For JavaScript one could think of an implementation of the already abovementioned Enumerable_first behavior which in its first iteration might look like the following code example:

var Enumerable_first = function () {
  this.first = function () {
    return this[0];
  };
};

Applying this Talent onto an array will provide the first ones behavior to the latter …

var list = ["foo", "bar", "baz"];

Enumerable_first.call(list);

console.log("list.first()", list.first()); // "foo"

… and operating at “class” level (Array.prototype), without conflict resolution, comes as close to a Mixin as possible …

var list = ["foo", "bar", "baz"];

Enumerable_first.call(Array.prototype);

console.log("list.first()", list.first()); // "foo"

The usage of this pattern at both “class” level and “instance” level was rediscovered by Angus Croll in 2011 and named Flight Mixin.

Even though this first approach works, it has some shortcomings that need to be improved. Right now every object the Talent was applied to, features its very own first method.

delete Array.prototype.first;

var
  list = ["foo", "bar", "baz"],
  arr = ["biz", "buzz"]
;
Enumerable_first.call(list);
Enumerable_first.call(arr);

console.log("arr.first()", arr.first()); // "biz"
console.log("(list.first === arr.first)", (list.first === arr.first)); // false

In order to make the first behavior shared code, the Talent implementation has to be wrapped into a module pattern - second code iteration:

var Enumerable_first = (function () {

  var
    Mixin,

    first = function () {
      return this[0];
    }
  ;
  Mixin = function () {
    this.first = first;
  };

  return Mixin;

}());

var
  list = ["foo", "bar", "baz"],
  arr = ["biz", "buzz"]
;
Enumerable_first.call(list);
Enumerable_first.call(arr);

console.log("list.first()", list.first());  // "foo"
console.log("arr.first()", arr.first());    // "biz"

console.log("(list.first === arr.first)", (list.first === arr.first)); // true

Having proved the last approach, it can be shortened within a third iteration to …

var Enumerable_first = (function () { // Pure Talent / Lean Mixin.
  var first = function () {
    return this[0];
  };
  return function () { // Mixin mechanics refering to shared code.
    this.first = first;
  };
}());

»Pure Talent« and »Lean Mixin«

Having come that fare one could phrase …

Every implementation of a Role Pattern in JavaScript should be called Talent.

… and …

Every module based stateless implementation of a Role that supports code sharing and does not provide conflict resolution should be referred to as Pure Talent and/or Lean Mixin

»Trait« bases evolved from »Mixin« approaches

A logical next step was to provide conflict resolution by a Resolvable Lean Mixin that implements composition methods like resolveBefore, resolveAfter, resolveAround and resolveWithAlias.

Due to having to resolve conflicts of competing/identical methods some additional supporting source code needs to be provided in order to make the given examples run without failing.

The code of a Resolvable approach based on a Lean Mixin then most probably will look like the next given example:

var Resolvable = (function () { // Pure Talent / Lean Mixin.

  var
    Mixin,

    isString    = function (type) {
      return (typeof type == "string");
    },
    isFunction  = function (type) {
      return (typeof type == "function");
    },

    resolveBefore = function (methodName, rivalingMethod) {
      if (isString(methodName) && isFunction(rivalingMethod)) {

        var type = this;
        if (isFunction(type[methodName])) {

          type[methodName] = type[methodName].before(rivalingMethod, type);
        } else {
          type[methodName] = rivalingMethod;
        }
      }
    },
    resolveAfter = function (methodName, rivalingMethod) {
      if (isString(methodName) && isFunction(rivalingMethod)) {

        var type = this;
        if (isFunction(type[methodName])) {

          type[methodName] = type[methodName].after(rivalingMethod, type);
        } else {
          type[methodName] = rivalingMethod;
        }
      }
    },
    resolveAround = function (methodName, rivalingMethod) {
      if (isString(methodName) && isFunction(rivalingMethod)) {

        var type = this;
        if (isFunction(type[methodName])) {

          type[methodName] = type[methodName].around(rivalingMethod, type);
        } else {
          type[methodName] = rivalingMethod;
        }
      }
    },

    resolveWithAlias = function (methodName, aliasName, rivalingMethod) {
      if (
        isString(methodName)
        && isString(aliasName)

        && (methodName != aliasName)

        && isFunction(rivalingMethod)
      ) {
        this[aliasName] = rivalingMethod;
      }
    }
  ;

  Mixin = function () { // Mixin mechanics refering to shared code.
    var type = this;

    type.resolveBefore    = resolveBefore;
    type.resolveAfter     = resolveAfter;
    type.resolveAround    = resolveAround;
    type.resolveWithAlias = resolveWithAlias;
  };

  return Mixin;

}());

The Resolvable’s use case for example then might look like this:

var Floatable = function () { // implementing a [Floatable] Trait.

  this.resolveAfter("initialize", function () {
    console.log("make it floatable.");
  });
};
var Ridable = function () { // implementing a [Ridable] Trait.

  this.resolveBefore("initialize", function () {
    console.log("make it ridable.");
  });
};

var Amphicar = function () {  // implementing an [Amphicar] Constructor.

  Resolvable.call(this);      // applying the [Resolvable] Mixin.

  Floatable.call(this);       // applying the [Floatable] Trait.
  Ridable.call(this);         // applying the [Ridable] Trait.

  this.initialize();
};

var ac = new Amphicar;
// "make it ridable."
// "make it floatable."

console.log(Object.keys(ac));
// ["resolveBefore", "resolveAfter", "resolveAround", "resolveWithAlias", "initialize"]

As one can see the purely implemented Resolvable Talent results in 4 accessible methods an Amphicar instance really does not need to expose.

Yet, the pure function based delegation approach in JavaScript is sustainable and capable of providing a solution that still implements all of a Resolvable’s composition methods whilst keeping them private.

What could be called a Hidden/Undercover Talent or Silent/Stealth Mixin does not mutate the object it is working on. Its main goal is preserving this objects context and delegation of this context to behavior that never will be exposed into public. For this such a Mixin augments an injected (local) proxy object by the aforementioned delegating behavior.

The »Stealth Mixin« approach of the Resolvable_silent variant will hide its methods from any Amphicar instance as it will be immediately demonstrated:

var Resolvable_silent = (function () { // Undercover Talent / Stealth Mixin.

  var
    StealthMixin,

    isString    = function (type) {
      return (typeof type == "string");
    },
    isFunction  = function (type) {
      return (typeof type == "function");
    },

    resolveBefore = function (methodName, rivalingMethod) {
      if (isString(methodName) && isFunction(rivalingMethod)) {

        var type = this;
        if (isFunction(type[methodName])) {

          type[methodName] = type[methodName].before(rivalingMethod, type);
        } else {
          type[methodName] = rivalingMethod;
        }
      }
    },
    resolveAfter = function (methodName, rivalingMethod) {
      if (isString(methodName) && isFunction(rivalingMethod)) {

        var type = this;
        if (isFunction(type[methodName])) {

          type[methodName] = type[methodName].after(rivalingMethod, type);
        } else {
          type[methodName] = rivalingMethod;
        }
      }
    },
    resolveAround = function (methodName, rivalingMethod) {
      if (isString(methodName) && isFunction(rivalingMethod)) {

        var type = this;
        if (isFunction(type[methodName])) {

          type[methodName] = type[methodName].around(rivalingMethod, type);
        } else {
          type[methodName] = rivalingMethod;
        }
      }
    },

    resolveWithAlias = function (methodName, aliasName, rivalingMethod) {
      if (
        isString(methodName)
        && isString(aliasName)

        && (methodName != aliasName)

        && isFunction(rivalingMethod)
      ) {
        this[aliasName] = rivalingMethod;
      }
    }
  ;

  StealthMixin = function (resolvableProxy) { // Stealth Mixin using an injected proxy.
    if (resolvableProxy && (typeof resolvableProxy == "object")) {

      var type = this;
  /*
   *  special functional Mixin pattern that preserves and foldes
   *  two different contexts and does delegation with partly code reuse.
   */
      resolvableProxy.resolveBefore    = function (/*methodName, rivalingMethod*/) {
        resolveBefore.apply(type, arguments);
      };
      resolvableProxy.resolveAfter     = function (/*methodName, rivalingMethod*/) {
        resolveAfter.apply(type, arguments);
      };
      resolvableProxy.resolveAround    = function (/*methodName, rivalingMethod*/) {
        resolveAround.apply(type, arguments);
      };
      resolvableProxy.resolveWithAlias = function (/*methodName, aliasName, rivalingMethod*/) {
        resolveWithAlias.apply(type, arguments);
      };
    }
  };

  return StealthMixin;

}());

The already given Resolvable’s use case from above has to be slightly changed to …

var Floatable = function () { // implementing a "silent" [Floatable] Trait featuring ...
                              // ... the "Stealth Mixin" variant of [Resolvable].
  var resolvable = {};
  Resolvable_silent.call(this, resolvable);

  resolvable.resolveAfter("initialize", function () {
    console.log("initialize :: make this floatable.", this);
  });
};
var Ridable = function () { // implementing a "silent" [Ridable] Trait featuring ...
                            // ... the "Stealth Mixin" variant of [Resolvable].
  var resolvable = {};
  Resolvable_silent.call(this, resolvable);

  resolvable.resolveBefore("initialize", function () {
    console.log("initialize :: make this ridable.", this);
  });
};

var Amphicar = function () {  // implementing the [Amphicar] Constructor.

  Floatable.call(this);       // applying the "silent" [Floatable] Trait.
  Ridable.call(this);         // applying the "silent" [Ridable] Trait.

  this.initialize();
};

var ac = new Amphicar;
// initialize :: make this ridable.   Amphicar {initialize: function}
// initialize :: make this floatable. Amphicar {initialize: function}

console.log("the new amphicar", ac);
// the new amphicar                   Amphicar {initialize: function}

… and does prove the function based concepts of Stealth Mixins and Silent Traits.

»Undercover Talent«, »Stealth Mixin« and »Silent Trait«

Every implementation of a Role that does not mutate its this context but augments an additionally injected proxy with behavior that folds delegation and both contexts in order to not directly expose the behavior into public should be referred to as Hidden/Undercover Talent and/or Silent/Stealth Mixin.

.

There are implementations of conflict resolving composition methods that are based on the Undercover Talent / Stealth Mixin pattern, called Resolvable.

.

Every implementation of a Role that is a composite of at least one Talent and one Stealth Mixin based Resolvable should be referred to as Silent Trait.

A current Pattern Taxonomy of function based »Talents«

Focusing on state its availability and origin, and how state then gets mutated or augmented whilst providing real world JavaScript examples, will be the guidline for the rest of this paper for it might be the most helpful way for catching up on this special matter.

»Taxonomy Matrix« of some possible Talent Patterns

Earlier papers and presentations of mine came up with kind of a »Trait Taxonomy Matrix« for the many Trait / Mixin variations, as a direct result of trying to classify the most used Role implementations in JavaScript, I derieved from practice.

Since the usage of terms like »Trait« and »Mixin« does not fully cover what really can be achieved with JavaScript, »Talents« as concept and term has become the new working base for all of those already identified code reuse variants. Based on the following state coordinates, the most recent wording therefore had to be changed completely to …

state stateless no active mutation actively mutating proxy augmentation
stateless Pure Talent
Lean Mixin
- - -
created - Smart Talent
(Skilled Talent)
Smart Talent
(Skillful Talent)
-
injected - Smart Talent
(Promoted Talent)
Smart Talent
(Privileged Talent)
-
both created and injected - Smart Talent
(Privileged Talent)
Smart Talent
(Privileged Talent)
-
injected proxy - - - Hidden/Undercover Talent
Silent/Stealth Mixin

The provided »Taxonomy Matrix« is a moving target and the currently used taxonomy is encouraged to be reviewed by everyone who is or feels opinionated about this topic.

»Smart Talent« Patterns

Whereas the Pure Talent is not aware of state at all and a Hidden/Undercover Talent has its own strict definition, every variation of a Smart Talent needs to carry state, be it created within itself and/or injected into it, actively mutating its state(s and/) or even not.

Every implementation variant of a Role that carries state regardless if injected or self defined and regardless of such states mutability should be referred to as Smart Talent.

There will be more than 6 variants of the Smart Talent pattern. And it heavily has to be overthought and discussed if it is really meaningful and of any use to distinguish and name all these variants. But there are different use cases for at least 2 of them.

An implementation of a Role that relies on additionally injected state but does only read and never does mutate it should be referred to as Promoted Talent.

Practical use cases then probably might be firstly a wrapper for implementing customized enumerable lists, Enumerable_first_last_item_listWrapper

var Enumerable_first_last_item_listWrapper = (function () {

  var
    global = this,

    Talent,

    parse_float = global.parseFloat,
    math_floor  = global.Math.floor
  ;
  Talent = function (list) { // implementing the "promoted" [Enumerable] Talent.
    var
      enumerable = this
    ;
    enumerable.first = function () {
      return list[0];
    };
    enumerable.last = function () {
      return list[list.length - 1];
    };
    enumerable.item = function (idx) {
      return list[math_floor(parse_float(idx, 10))];
    };
  };

  return Talent;

}).call(null);

… and secondly Allocable, a wrapper too, that controls outside access of enclosed list structures, and that already makes use of the firstly provided wrapper example …

var Allocable = (function () {

  var
    global = this,

    Enumerable_listWrapper = global.Enumerable_first_last_item_listWrapper,

    Talent,

    Array = global.Array,

    array_from = ((typeof Array.from == "function") && Array.from) || (function (proto_slice) {
      return function (listType) {

        return proto_slice.call(listType);
      };
    }(Array.prototype.slice))
  ;

  Talent = function (list) { // implementing the "promoted" [Allocable] Talent.
    var
      allocable = this
    ;
    allocable.valueOf = allocable.toArray = function () {
      return array_from(list);
    };
    allocable.toString = function () {
      return ("" + list);
    };
    allocable.size = function () {
      return list.length;
    };
    Enumerable_listWrapper.call(allocable, list);
  };

  return Talent;

}).call(this);

.., thus both together already do provide the base for e.g custom collection types like Queue:

var Queue = (function () {

  var
    global = this,
    Allocable = global.Allocable
  ;

  return function () { // implementing the [Queue] Constructor.
    var list = [];

    this.enqueue = function (type) {

      list.push(type);
      return type;
    };
    this.dequeue = function () {

      return list.shift();
    };

    Allocable.call(this, list); // applying the "promoted" [Allocable] Talent.
  };

}).call(null);



var q = new Queue;

console.log("q.size()", q.size());        // 0

q.enqueue("the");
q.enqueue("quick");
q.enqueue("brown");
q.enqueue("fox");

console.log("q.size()", q.size());        // 4
console.log("q.toArray()", q.toArray());  // ["the", "quick", "brown", "fox"]

console.log("q.first()", q.first());      // "the"
console.log("q.item(1)", q.item(1));      // "quick"
console.log("q.last()", q.last());        // "fox"

q.dequeue();
q.dequeue();

console.log("q.toArray()", q.toArray());  // ["brown", "fox"]
console.log("q.size()", q.size());        // 2

q.dequeue();
q.dequeue();
q.dequeue();

console.log("q.size()", q.size());        // 0

»Skilled Talent« and »Skillful Talent«

An implementation of a Role that does create mutable state on its own in order to solve its task(s) but does never rely on additionally injected state should be referred to as Skilled Talent or Skillful Talent

Observable probably is one of the most common real world examples for Mixins. The given example’s implementation is a Skillful Talent for it will at apply time create, read and mutate various states:

var Observable_SignalsAndSlots = (function () { // implementing a "skillful" [Observable] Talent.

  var
    global = this,
    Array = global.Array,

    isFunction = function (type) {
      return (
        (typeof type == "function")
        && (typeof type.call == "function")
        && (typeof type.apply == "function")
      );
    },
    isString = (function (regXCLassName, expose_implementation) {
      return function (type) {

        return regXCLassName.test(expose_implementation.call(type));
      };
    }((/^\[object\s+String\]$/), global.Object.prototype.toString)),

    array_from = ((typeof Array.from == "function") && Array.from) || (function (proto_slice) {
      return function (listType) {

        return proto_slice.call(listType);
      };
    }(Array.prototype.slice))
  ;

  var
    Event = function (target, type) {

      this.target = target;
      this.type = type;
    },

    EventListener = function (target, type, handler) {

      var defaultEvent = new Event(target, type);             // creation of new state.

      this.handleEvent = function (evt) {
        if (evt && (typeof evt == "object")) {

          evt.target = defaultEvent.target;
          evt.type = defaultEvent.type;

        } else {

          evt = {
            target: defaultEvent.target,
            type: defaultEvent.type
          };
        }
        handler(evt);
      };
      this.getType = function () {
        return type;
      };
      this.getHandler = function () {
        return handler;
      };
    },

    EventTargetMixin = function () { // implementing the [EventTarget] Mixin as "skillful" Talent.

      var eventMap = {};                                      // mutable state.

      this.addEventListener = function (type, handler) {      // will trigger creation of new state.
        var reference;
        if (type && isString(type) && isFunction(handler)) {
          var
            event = eventMap[type],
            listener = new EventListener(this, type, handler) // creation of new state.
          ;
          if (event) {
            var
              handlers = event.handlers,
              listeners = event.listeners,
              idx = handlers.indexOf(handler)
              ;
            if (idx == -1) {
              handlers.push(listener.getHandler());
              listeners.push(listener);

              reference = listener;
            } else {
              reference = listeners[idx];
            }
          } else {
            event = eventMap[type] = {};
            event.handlers = [listener.getHandler()];
            event.listeners = [listener];

            reference = listener;
          }
        }
        return reference;
      };

      this.dispatchEvent = function (evt) {
        var
          successfully = false,
          type = (
            (evt && (typeof evt == "object") && isString(evt.type) && evt.type)
            || (isString(evt) && evt)
          ),
          event = (type && eventMap[type])
        ;
        if (event) {
          var
            listeners = (event.listeners && array_from(event.listeners)),
            len = ((listeners && listeners.length) || 0),
            idx = -1
          ;
          if (len >= 1) {
            while (++idx < len) {

              listeners[idx].handleEvent(evt);
            }
            successfully = true;
          }
        }
        return successfully;
      };
    }
  ;

  return EventTargetMixin; // will be exposed to the outside as [Observable_SignalsAndSlots].

}).call(null);

The already provided Queue example in its next iteration step then could be improved to:

var Queue = (function () {

  var
    global = this,

    Observable  = global.Observable_SignalsAndSlots,
    Allocable   = global.Allocable,

    Queue,

    onEnqueue = function (queue, type) {
      queue.dispatchEvent({type: "enqueue", item: type});
    },
    onDequeue = function (queue, type) {
      queue.dispatchEvent({type: "dequeue", item: type});
    },
    onEmpty = function (queue) {
      queue.dispatchEvent("empty");
    }
  ;

  Queue = function () { // implementing the [Queue] Constructor.
    var
      queue = this,
      list = []
    ;
    queue.enqueue = function (type) {

      list.push(type);
      onEnqueue(queue, type);

      return type;
    };
    queue.dequeue = function () {

      var type = list.shift();
      onDequeue(queue, type);

      (list.length || onEmpty(queue));

      return type;
    };
    Observable.call(queue);       // applying the "skillful" [Observable_SignalsAndSlots] Talent.
    Allocable.call(queue, list);  // applying the "promoted" [Allocable] Talent.
  };

  return Queue;

}).call(null);



var q = new Queue;

q.addEventListener("enqueue", function (evt) {console.log("enqueue", evt);});
q.addEventListener("dequeue", function (evt) {console.log("dequeue", evt);});
q.addEventListener("empty", function (evt) {console.log("empty", evt);});

q.enqueue("the");   // "enqueue" Object {type: "enqueue", item: "the", target: Queue}
q.enqueue("quick"); // "enqueue" Object {type: "enqueue", item: "quick", target: Queue}
q.enqueue("brown"); // "enqueue" Object {type: "enqueue", item: "brown", target: Queue}
q.enqueue("fox");   // "enqueue" Object {type: "enqueue", item: "fox", target: Queue}

q.dequeue();        // "dequeue" Object {type: "dequeue", item: "the", target: Queue}
q.dequeue();        // "dequeue" Object {type: "dequeue", item: "quick", target: Queue}
q.dequeue();        // "dequeue" Object {type: "dequeue", item: "brown", target: Queue}
q.dequeue();        // "dequeue" Object {type: "dequeue", item: "fox", target: Queue}
                    // "empty"   Object {target: Queue, type: "empty"}
q.dequeue();        // "dequeue" Object {type: "dequeue", item: undefined, target: Queue}
                    // "empty"   Object {target: Queue, type: "empty"}

»Privileged Talent«

Every other remaining Smart Talent variant then should be called Privileged Talent which pretty much gets covered by the last definition:

An implementation of a Role that relies either on mutation of additionally injected state only or on both, creation of mutable state and additionally injected state, regardless if the latter then gets mutated or not, should be referred to as Privileged Talent.


Closing Words

The goal of this paper was to provide/share knowledge of code reuse patterns in JavaScript at this languages core level. Neither traits.js some years ago could convince me nor does it fully CocktailJS for both are, in my eyes, overambitious closed/monolithic libraries that do follow an object based approach, whereas I always strongly will argue that a function based approach is most natural for this languages design since it does provide delegation for free and at the same time also enables injecting of and passing around state.

The paper hopefully could prove that for JavaScript a pure function and delegation based approach, mostly accompanied by the module pattern, already enables Talent based composition, built upon Mixin mechanics and Trait specific functionality.

Most of real world JavaScript use cases are really not in need for far more complex programming approaches like being used by each of both aforementioned libraries. And making use of function based Talent patterns keeps ones code framework and library agnostic.

Staying self-restraint to only one base patterns that just varies the usage of core language features like Function, call and apply as much as closure, context and scope ensures a clean approach and results in lean and tailored Talent implementations.


Appendix

supporting source code:

Function.prototype.before = function (behaviorBefore, target) {
  var proceedAfter = this;

  return function () {
    var args = arguments;

    behaviorBefore.apply(target, args);
    return proceedAfter.apply(target, args);
  };
};
Function.prototype.after = function (behaviorAfter, target) {
  var proceedBefore = this;

  return function () {
    var args = arguments;

    proceedBefore.apply(target, args);
    return behaviorAfter.apply(target, args);
  };
};
Function.prototype.around = function (behaviorAround, target) {
  var proceedEnclosed = this;

  return function () {
    return behaviorAround.call(target, proceedEnclosed, behaviorAround, arguments, target);
  };
};

Function.modifiers.adviceTypes.before-after-around is a more sustainable implementation of the just provided basic code variant.


  • This document is shared in public via google drive and via blogspot. Everyone is allowed commenting on it. Invited people even have edit rights.

  • It also is available as gist.


Written with StackEdit.


  1. en.wikipedia.org: JavaScript; developer.mozilla.org: JavaScript
  2. javascriptweblog.wordpress.com: »A fresh look at JavaScript Mixins«
  3. webreflection.blogspot.de: »Flight Mixins Are Awesome!«
  4. petsel.github.io: »JavaScript Code Reuse Patterns«
  5. github.com/petsel/javascript-code-reuse-patterns: »Composition«
  6. developer.mozilla.org: this keyword
  7. developer.mozilla.org: Function Object
  8. developer.mozilla.org: Function.prototype.call
  9. developer.mozilla.org: Function.prototype.apply
  10. developer.mozilla.org: Inheritance and the prototype chain
  11. Software Composition Group (SCG), University of Bern: Trait
  12. SCG, University of Bern: »Traits: Composable Units of Behavior«
  13. Software Composition Group (SCG), University of Bern
  14. SCG, University of Bern: »Stateful Traits«
  15. SCG, University of Bern: SCG: Talents
  16. SCG, University of Bern: »Talents: Dynamically Composable Units of Reuse«

No comments:

Post a Comment