DEV Community

codelitically_incorrect
codelitically_incorrect

Posted on

Mixins in ECMAScript 6 - Awkward, inconvenient, powerful.

(UPDATE: I am really not sure about the state of proposals for 'mixins' in ECMAScript. The information below were snippets I got from another source in 2017. I do know we are able to support traits in ES6 by way of Object.assign(..) but I was hoping it would be keyworded-in as part of the class definition syntax for future ES. )


So you've heard that ECMAScript 6 will sport "Mixins". Some languages implement this feature as "Traits" or Shared Modules. The origin of the concept, I feel, was born in the language called SELF (a Sun Microsystems experiment, see: http://www.selflanguage.org/).

In a simple explanation, traits, or mixins, is a language feature allowing classes of objects to share a common set of behaviors we call methods -- or functions, perhaps even data properties. It is NOT inheritance, but some think of it as multiple inheritance. It is an arbitrary implementation for simple code reuse when inheritance does not make sense.

For example:
In building a game, the Character, Level or Weapon classes might all posses the ability to log messages to a console for debugging purposes, so a mixin called Loggable could be used to package up a few methods to be shared across the class hierarchy. This way, classes even though not related, might share this common trait. As you see, inheritance does not make sense logically or semantically. A Person is-not a Loggable. It does not sound right, it does not read right, it conflicts with the beauty of code and clarity.

here is the Ruby representation. It's a beauty.

module Loggable
   def log (msg)
      puts msg
   end
end

class Character include Loggable
   def jump
      log("jumped")
   end
end

class Sword < Weapon  include Loggable
   def animate_blade
      log("do sword animation");
   end
end

In Scala, It's a beauty:

class Sword extends Weapon with Loggable {
   def animate_blade() { 
      log("do sword animation")
   }
}

In ECMAScript 6:

var Loggable = Base => class extends Base {
  log(s) { }
};
class Weapon { }
class Sword extends Loggable(Weapon) { }

WTF?!!!!!
It gets ugly if you wanted 2 or more mix-ins:

class Weapon { }
class Sword extends Loggable(Localizable(Weapon)) { }

I don't know. It's powerful. Definitely not a beauty!

Top comments (1)

Collapse
 
petsel profile image
Peter Seliger • Edited

This pattern continues to function with inheritance through a technique that rather should be referred to as dynamic subclassing. This approach uses factory functions to create subclasses, where the factory generates an anonymous and ad-hoc implemented class that extends the provided super- or base-class.

However, a significant risk arises during its application: developers often resort to nested factory-function calls. For example, repeatedly invoking just something as simple as Localizable(Weapon) will always create an anonymous subclass each resembling LocalizableWeapon. Yet, none of these generated classes are identical or interchangeable, despite having the same implementation.

In order to avoid polluting or obfuscating the inheritance chain, developers must instead explicitly define each intermediate subclass, as follows:

const LocalizableWeapon = Localizable(Weapon);
const LoggableLocalizableWeapon = Loggable(LocalizableWeapon);
class Sword extends LoggableLocalizableWeapon { }
Enter fullscreen mode Exit fullscreen mode

This approach underscores a key limitation of the so-called "class-based mixin". This pattern is not a true mixin-based composition technique. Factories which follow this pattern are always entirely inheritance-based and will most probably contribute to the destabilization of prototype chains. As such, they should be more accurately referred to as dynamic subclassing factories, rather than mixins.