barbarian meets coding

WebDev, UX & a Pinch of Fantasy

Black Tower Summoning: Object Composition With Mixins

| Comments

The Mastering the Arcane Art of JavaScript-mancy series are my humble attempt at bringing my love for JavaScript to all other C# developers that haven’t yet discovered how awesome this language and its whole ecosystem are. These articles are excerpts of the super duper awesome JavaScript-Mancy book a compendium of all things JavaScript for C# developers.

In the previous articles of this series you have dived into Object Oriented Programming in JavaScript. You’ve learned about object initializers, factory functions, about constructor functions, prototypical inheritance, polymorphism, how to mimick C# classes with ES5 and even about ES6 classes.

So far we’ve kept ourselves very close to techniques and concepts that you are accustomed to in C# so as to make you a comfortable inhabitant of the world of JavaScript. So! Now that you are almost a mighty JavaScript-mancer we’re going to let go of C# for a little while and embrace the dynamic nature of JavaScript. Welcome to a world of flexibility and freedom! The realm of object composition!

This time by the hand of Mixins.

The Problem With Classes and Classical Inheritance

The world we live in is unbelievably complex. Creating software that handles this infinite degree of complexity and detail is a futile endeavor. Object Oriented Programming attempts to solve this problem by abstracting the world inside a problem space and creating representations of reality that are simplified and that let us solve very specific problems. Thus turning the impossible into something, if not simple, at least manageable.

These OOP representations are usually done with the aid of classes and objects. A class represents a blueprint of something, some entity in reality that we want to abstract and use in our programs. It determines which properties we are going to use to represent that entity and which actions it can perform. An object represents a particular instance of that blueprint, of that class, something that exists, has a particular state and can be operated on.

This is all well and good. We live in a complex world and OOP helps us manage that complexity through simplified versions of reality called classes and objects.

A side effect of using classes is that it creates a taxonomy or a classification of the entities in the world around us. This classification, because of the nature of classes and inheritance, tends to be a very rigid one that doesn’t tolerate change very well. Change not strictly in the sense of adding a new property to a class but in accommodating the system of classes to new knowledge about the problem domain.

We saw an example of this particular problem in “Introduction to Object-Oriented Programming in JavaScript” (anoher article in the series) where we defined these three classes Minion, Wizard and Thief:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Minion {
  constructor(name, hp){
    this.name = name;
    this.hp = hp;
  }
  toString(){
    return this.name;
  }
}

class Wizard extends Minion {
  constructor(element, mana, name, hp){
    super(name, hp);
    this.element = element;
    this.mana = mana;
  }
  toString(){
    return super.toString() + ", the " + this.element +" Wizard";
  }
  castsSpell(spell, target){
    console.log(this + ' casts ' + spell + ' on ' + target);
    this.mana -= spell.mana;
    spell(target);
  }
}

class Thief extends Minion {
  constructor(name, hp){
    super(name, hp);
  }
  toString(){
    return super.toString() + ", the Thief";
  }
  steals(target, item){
    console.log(`${this} steals ${item} from ${target}`);
  }
}

And a problem space in which we have a Wizard as someone who can cast spells, and a Thief as someone who can steal. In this context we learned something new about our domain, the existence of Bards as creatures of myth that could both casts spells, steal, and even play an instrument!

But the system of classes that we had created, our current taxonomy that represents our view of the world right now doesn’t accommodate very well the idea of a Bard… is it a Wizard? Is it a Thief? Is it something else? It cannot be both! Can it!? And so in order to bring this knowledge into our system, we need to do a major redesign of our classes, redefine our whole taxonomy, or duplicate code and forsake the benefits that could come from polymorphism.

So classes create taxonomies. Taxonomies are rigid and don’t tolerate change well. Is there anything else? Well there’s also a problem with the when, when do we create these classes and taxonomies? And how does that affects how we work?

A very wise person, Douglas Crockford, reflected over this and realized that we build these taxonomies when we start a project, which is the moment when we least know about our domain and problem space. We are creating these very rigid systems to represent a domain we are usually not familiar with. Systems that will inevitably need to change as we find out more about the domain but that are not well suited to adapt to change.

Is there a better way?

Well, yes sir there is. It is class-free object oriented programming and Douglas calls it the single most important contribution of JavaScript to humanity 1. In this article and the next we will focus in how you can achieve this approach to object oriented programming, where classes disappear and we just focus on objects. Sounds exciting, doesn’t it?

Free Yourself From Classes With Object Composition and Mixins

You can experiment with all examples in this article directly within this jsBin.

In Introduction to Object-Oriented Programming in JAvaScript you had a taste of class-free inheritance when you learned how to compose objects with each other using Object.assign. In that particular implementation of class-free inheritance we defined behaviors as objects canBeIdentifiedByName, canCastSpells, canSteal and canPlayMusic:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
let canBeIdentifiedByName = {
  toString(){
    return this.name;
  }
};

let canCastSpells = {
  castsSpell(spell, target){
    console.log(this + ' casts ' + spell + ' on ' + target);
    this.mana -= spell.mana;
    spell(target);
  }
};

let canSteal = {
  steals(target, item){
    console.log(`${this} steals ${item} from ${target}`);
  }
};

let canPlayMusic = {
  playsMusic(){
    console.log(`${this} grabs his ${this.instrument} and starts playing music`);
  }
};

And we composed them together to build more complex objects:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// and now we can create our objects by composing this behaviors together
function Wizard(element, mana, name, hp){
  let wizard = {element,
                mana,
                name,
                hp};
  Object.assign(wizard,
               canBeIdentifiedByName,
               canCastSpells);
  return wizard;
}

function Thief(name, hp){
  let thief = {name,
               hp};
  Object.assign(thief,
               canBeIdentifiedByName,
               canSteal);
  return thief;
}

function Bard(instrument, mana, name, hp){
  let bard = {instrument,
                mana,
                name,
                hp};
  Object.assign(bard,
               canBeIdentifiedByName,
               canCastSpells,
               canSteal,
               canPlayMusic);
  return bard;
}

That work just like you would expect of a wizard, a thief and a bard:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var lightningSpell = function(target){
  console.log('A bolt of lightning electrifies ' + target + '(-10hp)');
  target.hp -= 10;
};
lightningSpell.mana = 5;
lightningSpell.toString = function(){ return 'lightning spell';};
var orc = {name: 'orc', hp: 100, toString(){return this.name}};

let wizard = Wizard('fire', 100, 'Randalf, the Red', 10);
wizard.castsSpell(lightningSpell, orc);
// => Randalf, the Red casts lightning spell on orc
// => A bolt of lightning electrifies orc(-10hp)

let thief = Thief('Locke Lamora', 100);
thief.steals('orc', /*item*/ 'gold coin');
// => Locke Lamora steals gold coin from orc

let bard = Bard('lute', 100, 'Kvothe', 100);
bard.playsMusic();
// => Kvothe grabs his lute and starts playing music
bard.steals('orc', /*item*/ 'sandwich');
// => Kvothe steals sandwich from orc
bard.castsSpell(lightningSpell, orc);
// => Kvothe casts lightning spell on orc
// =>A bolt of lightning electrifies orc(-10hp)

The objects that encapsulate a bit of reusable behavior (canSteal, canPlayMusic, etc) is what we call mixins. We compose them, or mix them, with other objects to provide them with additional behavior. You don’t need to use a factory function like in the previous examples, you can compose a simple object if so you wish:

1
2
3
4
5
6
7
8
9
var orcMagician = Object.assign(
    {name: 'orcmag', hp: 100, mana: 50},
    canBeIdentifiedByName,
    canCastSpells);

orcMagician.castsSpell(lightningSpell, wizard);
// => orcmag casts lightning spell on Randalf, the Red
// => A bolt of lightning electrifies Randalf, the Red(-10hp)
// sweet vengeance muahahaha

The factory function just adds that extra level of convenience to create many objects.

Let’s continue strengthening this idea of object composition and flexibility with a new example. Imagine that you want to be able to see your legions displayed on a map so you can take better strategic decisions in your path to ruling the known universe. Well, you can define a canBePositioned object that encapsulates this behavior that you need:

1
2
3
4
5
6
7
8
9
var canBePositioned = {
    x : 0,
    y : 0,
    movesTo(x, y) {
        console.log(`${this} moves from (${this.x}, ${this.y}) to (${x}, ${y})`);
        this.x = x;
        this.y = y;
    }
};

And augment all of our minions with that functionality:

1
2
3
4
Object.assign(wizard, canBePositioned);
Object.assign(thief, canBePositioned);
Object.assign(bard, canBePositioned);
Object.assign(orcMagician, canBePositioned);

All of the sudden we can position and move them to our heart’s content. And if we define a very simple ASCII two-dimensional map like this one:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function Map(width, height, creatures){

  function paintPoint(x,y){
    var creatureInPosition = creatures.find(c => c.x === x && c.y === y);
    if (creatureInPosition) return creatureInPosition.name[0];
    return '_';
  }

  return {
    width,
    height,
    creatures,
    paint(){
      let map = '';
      for(let y = 0; y < height; y++){
        for (let x = 0; x < width; x++)
          map += paintPoint(x,y);
        map += '\n';
      }
      return map;
    }
  }
}

We can combine the Map capability of drawing stuff and the minions capability of positioning themselves and moving around to get a tactical representation of our army:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
wizard.movesTo(10,10);
// => Randalf, the Red moves from (0, 0) to (10, 10)
thief.movesTo(5,5);
// => Locke Lamora moves from (0, 0) to (5, 5)
bard.movesTo(15,15);
// => Kvothe moves from (0, 0) to (15, 15)

let worldMap = Map(50, 20, [wizard, thief, bard, orcMagician]);
console.log(worldMap.paint());

/* =>
o_________________________________________________
__________________________________________________
__________________________________________________
__________________________________________________
__________________________________________________
_____L____________________________________________
__________________________________________________
__________________________________________________
__________________________________________________
__________________________________________________
__________R_______________________________________
__________________________________________________
__________________________________________________
__________________________________________________
__________________________________________________
_______________K__________________________________
__________________________________________________
__________________________________________________
__________________________________________________
__________________________________________________
*/

You may be thinking… Well, I can do this with C# and classical inheritance any day. And indeed you can, but some interesting ideas about the object composition approach are that:

  • We don’t need any upfront design effort to make our application extensible. In C# you need to define the extensibility points of a system because you need to use the right artifacts like interfaces, composition over inheritance, design patterns like strategy, etc. In JavaScript we don’t need to over-architect our solution, or carefully design our application for extensibility purposes. You get a new feature, you define a new behavior, compose it with your existing objects and start using it.
  • Object composition happens at runtime. You have your program running, your objects doing whatever objects do and all of the sudden BOOM! Object composability and your objects get new features and can do new interesting things. New things like changing from a text representation to a 2D representation or a 3D representation and who knows what more.
  • It doesn’t need to affect the original objects at all. You can keep your objects as they are, clone them and apply the composition on the clones. This can enable interesting approaches like having different bounded contexts (like in DDD2) with slightly diverse domain models adapted to a particular context needs and goals.
  • You can compose an object with many other objects representing different behaviors (like a multiple inheritance of sorts). This tends to be harder to do in classical inheritance based languages like C# where you are limited to a single base class or to a flavor of composition that requires a lot of boilerplate code, forward planning and design.

With object composition we achieve this true plug and play solution where you can combine domain objects with behaviors in very interesting and flexible ways. By the way, this type of object composition is another type of prototypical inheritance that we called concatenative inheritance earlier in this part of the series.

Limitations of Mixins as Objects

As wonderful as mixin objects are they have some severe limitations:

  • They don’t support data privacy
  • They can create undesired coupling between objects
  • They are subject to name collisions

Let’s take a closer look at each of these.

Mixin objects don’t support true data privacy because they don’t support closures. You can use ES6 symbols though and keep your symbols tucked away within a module where you define your mixins.

If you are not careful, mixin objects can create undesired coupling between disparate objects. For instance, if you use the same mixin to extend several other objects, because the extending consists it copying properties, you can end up having several objects that have a reference to the same object. This will result in undesired side-effects and a horrible source of bugs.

You can appreciate this problem when we change the canBePositioned example to use a position object instead of separate x and y properties:

1
2
3
4
5
6
7
8
var canBePositionedWithGotcha = {
    position: {x: 0, y: 0},
    movesTo(x, y) {
        console.log(`${this} moves from (${this.position.x}, ${this.position.y}) to (${x}, ${y})`);
        this.position.x = x;
        this.position.y = y;
    }
};

When you compose the wizardOfOz and tasselhof with this mixin the position object is shared between them both. This results in any minion moving affecting the other, a characteristic that you definitely want to avoid:

1
2
3
4
5
6
7
8
9
10
var wizardOfOz = Wizard('oz', 100, 'Wizard of Oz', 10);
var tasselhof = Thief('Tasshelhof B.', 20);
Object.assign(wizardOfOz, canBePositionedWithGotcha);
Object.assign(tasselhof, canBePositionedWithGotcha);

wizardOfOz.movesTo(2,2);
// => Wizard of Oz moves from (0, 0) to (2, 2)
tasselhof.movesTo(6,6);
// => Tasshelhof B. moves from (2, 2) to (6, 6)
// wait... from (2,2)?????

Object mixins are also subject to property name collisions. Trying to compose an object with two mixins with the same properties but different interfaces can lead to errors:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var canBePositionedIn3Dimensions = {
    x: 0,
    y: 0,
    z: 0,
    movesTo(x, y, z) {
        console.log(`${this} moves from (${this.x}, ${this.y}, ${this.z}) to (${x}, ${y}, ${z})`);
        this.x = x;
        this.y = y;
        this.z = z;
    }
};

var raist = Wizard('death', /*mana*/ 1000, 'Raistlin', /*hp*/ 1);
Object.assign(raist, canBePositioned, canBePositionedIn3Dimensions);

// we used the movesTo method thinking about the canBePositioned mixin
// and we get an unexpected result z becomes undefined
raist.movesTo(10, 20);
// => Raistlin moves from (0, 0, 0) to (10, 20, undefined)

Is there a way to surpass these limitations? Indeed there is! Behold! Functional mixins!

Functional Mixins

Functional mixins are mixins implemented as functions instead of objects. They let you implement data privacy through closures and avoid undesired coupling between objects by working as a factory for mixins.

Let’s see how we can turn our previously defined behaviors from mixin objects to functional mixins:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let canCastSpellsFn = (state) => ({
  castsSpell(spell, target){
    console.log(`${state.name} casts ${spell} on ${target}`);
    state.mana -= spell.mana;
    spell(target);
  }
});

let canStealFn = (state) => ({
  steals(target, item){
    console.log(`${state.name} steals ${item} from ${target}`);
  }
});

let canPlayMusicFn = (state) => ({
  playsMusic(){
    console.log(`${state.name} grabs his ${state.instrument} and starts playing music`);
  }
});

In this example the canCastSpell mixin and its companions have been rewritten as functions. These functions take a state argument that represents the state of the object that the mixins are going to extend and use it to provide it with new functionality. This functional implementation comes with two advantages:

  • Because the state object is passed as a argument to the mixin it can remain private between object and mixin.
  • Because each time the functional mixin object is called it returns a new object there’s no problem with coupling state between objects (as it happened with mixin objects).

Having redefined our behaviors we can redefine the wizards, thiefs and bards in terms of them:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function TheWizard(element, mana, name, hp){
  let state = {element,
               mana,
               name,
               hp};

  return Object.assign({},
               canBeIdentifiedByNameFn(state),
               canCastSpellsFn(state));
}

function TheThief(name, hp){
  let state = {name,
               hp};

  return Object.assign({},
                       canBeIdentifiedByNameFn(state),
                       canStealFn(state));
}

function TheBard(instrument, mana, name, hp){
  let state = {instrument,
                mana,
                name,
                hp};

  return Object.assign({},
               canBeIdentifiedByNameFn(state),
               canCastSpellsFn(state),
               canStealFn(state),
               canPlayMusicFn(state));
}

And use them as we have done in previous examples:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var landaf = TheWizard('light', 100, 'Landaf the light', 100);
landaf.castsSpell(lightningSpell, orc);
// => Landaf the light casts lightning spell on orc
// => A bolt of lightning electrifies orc(-10hp)

var lupen = TheThief('Lupen', 200);
lupen.steals(orc, 'rusty copper ring');
// => Lupen steals rusty copper ring from orc

var bart = TheBard('lute', 200, 'Bart', 100);
bart.playsMusic();
// => Bart grabs his lute and starts playing music
bart.steals(lupen, 'rusty copper ring');
// => Bart steals rusty copper ring from Lupen
bart.castsSpell(lightningSpell, landaf);
// => Bart casts lightning spell on Landaf the light
// => A bolt of lightning electrifies Landaf the light(-10hp)
// Wow Bart is mean!

In this example we have separated the internal state of every object, represented with the state object, from its public API which is returned by the factory function.

The internal state of the object is passed as an argument to the different functional mixins and therefore they gain access to it.

The public API is defined by extending an empty object with the objects resulting from applying the different functional mixins to the state object. This empty object could also include a subset or all of the variables contained in state and, in that case, they would become public:

1
2
3
4
5
6
7
8
9
10
11
12
function TheBard(instrument, mana, name, hp){
  let state = {instrument,
                mana,
                name,
                hp};

  return Object.assign(state,
               canBeIdentifiedByNameFn(state),
               canCastSpellsFn(state),
               canStealFn(state),
               canPlayMusicFn(state));
}

As you’ve appreciated in these examples, functional mixins solve the limitations of mixin objects in terms of data privacy and coupling between extended objects.

There’s still one limitation to contend with which are name collisions, that is, the possibility that two mixins provide behaviors with the same name. You can handle name collisions in two ways: live with them or use namespacing.

Since Object.assign works overwriting object properties from right to left you can embrace this feature and take advantage of it when you need to replace behaviors with new ones:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let canCastSpellsOnMany = {
  castsSpell(spell, ...many){
    many.forEach(target => {
      console.log(this + ' casts ' + spell + ' on ' + target);
      this.mana -= spell.mana;
      spell(target);
    });
  }
}

Object.assign(bard, canCastSpellsOnMany);
bard.castsSpell(lightningSpell, orc, orcMagician, landaf);
// => Kvothe casts lightning spell on orc
// => A bolt of lightning electrifies orc (-10hp)
// => Kvothe casts ligtning spell on orcmag
// => A bolt of lightning electrifies orcmag (-10hp)
// => Kvothe casts lightning spell on Landaf the light
// => A bolt of lightning electrifies Landaf the light(-10hp)

Or prevent this from happening by namespacing each mixin that is composed with an object. This will result in a less natural and more verbose API for the resulting objects:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var canEat = {
  food: {
    eats(foodItem) {
      console.log(`${this} eats ${foodItem}`);
      this.hp += foodItem.recoverHp;
    }
  }
};
// bard.food.eats({name: 'banana', recoverHp: 10, toString(){return this.name;}});

var canEatMany = {
  foods: {
    eats(...foodItems) {
      foodItems.forEach(f => {
        console.log(`${this} eats ${f}`);
        this.hp += f.recoverHp;
      });
    }
  }
};

// bard.foods.eats(
//    {name: 'banana', recoverHp: 10, toString(){return this.name;}}
//    {name: 'sandwich', recoverHp: 20, toString(){return this.name;}});

Combining Mixins with ES6 Classes

If you are still not convinced about the usefulness and simplicity of vanilla objects and mixins you can continue using ES6 classes and combine them with mixins. That way you get a comfortable path from C# into JavaScript, a familiar pattern for defining your domain model and additionally you gain a fantastic way to reuse code and behaviors via mixins.

You have two options when combining ES6 classes with mixins. You can either compose your already instantiated objects with a mixin on a per-case basis, or you can compose your class prototype with a mixin and automatically provide all existing and future instances of that class with new behaviors.

Let’s imagine we have a Warrior class that will help us illustrate this with an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Warrior{

  constructor(name, hp=500){
    this.name = name;
    this.hp = hp;
    this.weapons = [];
  }

  equipsWeapon(weapon){
    weapon.isEquipped = true;
    this.weapons.push(weapon);
    console.log(`${this} picks ${weapon} from the ground and looks at it appreciatively`);
  }

  attacks(target){
    if (this.weapons.length === 0) {
      console.log(`${this} attacks ${target} with his bare arms`);
    } else {
      console.log(`${this} attacks ${target} with ${this.weapons.find(w => w.isEquipped)}`);
    }
  }

  toString(){
    return this.name;
  }

}

The first option is very straightforward because we are just composing an object with another object. We can instantiate a new fearful warrior caramon:

1
let caramon = new Warrior('Caramon', 1000);

And now, if we want this specific warrior to be able to steal, we compose it with the canSteal mixin from previous examples:

1
2
3
Object.assign(caramon, canSteal);
caramon.steals(bard, 'lute');
// => Caramon steals lute from Kvothe

Alternatively, we can compose the Warrior class prototype with a mixin and provide all existing and future instances of this class with new behavior. This is an excellent way to define a series of reusable behaviors and compose them with our domain model classes as we see fit.

For instance, let’s make all warriors capable of being positioned in a map via the canBePositioned mixin:

1
Object.assign(Warrior.prototype, canBePositioned);

We can easily verify how indeed both already define warriors like caramon and new warriors like riverwind can move around in a two dimensional space:

1
2
3
4
5
6
7
8
9
// existing instances of Warrior now can be positioned
caramon.movesTo(10,10);
// => Caramon moves from (0, 0) to (10, 10)
// Crazy!

// and new ones as well
let riverwind = new Warrior('Riverwind', 300);
riverwind.movesTo(20,20);
// => Riverwind moves from (0, 0) to (20, 20)

Object.assign in Depth

We’ve used Object.assign a lot during this article and even in previous articles. We know that it copies properties from several source objects into a target object. But does it copy all properties? What about the properties within an object prototype? Does it do a deep copy of the source objects? Or just a shallow copy? That’s what we’ll answer to in this section.

The Object.assign method is a new Object static method in ES6 that lets you copy all enumerable own properties from one or several source objects into a single target object.

1
2
3
4
5
6
7
8
9
10
// copy properties from one object to another
var companyOfTheRing = { aHobbit: 'frodo'};
var companyPlusOne = Object.assign(/* target */ companyOfTheRing, /*source*/ { aWizard: 'Gandalf'});
console.log(companyOfTheRing);
// => [object Object] { aHobbit: "frodo", aWizard: "Gandalf" }

// merge serveral objects into one 
Object.assign(companyOfTheRing, {anElf: 'Legolas'}, {aDwarf: 'Gimli'});
console.log(companyOfTheRing);
// => [objetc Object] {aHobbit: "frodo", ... anElf: "Legolas", aDwarf: "Gimli"}

The target object is the first argument to Object.assign but it is also returned by it:

1
2
3
// the returned object is the same as the target object
console.log(`companyPlusOne and companyOfTheRingt are the same: ${companyPlusOne === companyOfTheRing}`);
// => companyPlusOne and companyOfTheRingt are the same: true

If you don’t want to mutate any of your existing objects, you can use a new object {} as target:

1
2
3
4
// clone an object (shallow-copy)
var clonedCompany = Object.assign(/*target*/ {}, /*source*/ companyOfTheRing);
console.log(clonedCompany);
// => [objetc Object] {aHobbit: "frodo", ... anElf: "Legolas", aDwarf: "Gimli"}

Object.assign only copies properties from the source object itself and not from its prototype:

1
2
3
4
5
6
7
8
9
10
11
var newCompanyWithPrototype = Object.assign({
  '__proto__': {
    destroyTheRing(){
      console.log('The mighty company of the ring successfully' +
                  'destroys the ring and saves Middle Earth');
    }
  }}, companyOfTheRing);

var companyOfTheBracelet = Object.assign({}, newCompanyWithPrototype);
console.log(`companyOfTheBracelet.destroyTheRing: ${companyOfTheBracelet.destroyTheRing}`);
// => companyOfTheBracelet.destroyTheRing: undefined

It performs a shallow copy of the source object properties. That is, if your source object has a property that is an object, the target object will gain a new property that will point to that same object.

1
2
3
4
5
6
7
// it does a shallow-copy of each property
companyOfTheRing.equipment = ['bread', 'rope', 'the one ring'];
var companyOfTheSash = Object.assign({}, companyOfTheRing);
companyOfTheSash.equipment.push('sash');
console.log(companyOfTheRing.equipment);
// => ["bread", "rope", "the one ring", "sash"]
// ups!

Enumerable Properties?

Enumerability is an internal characteristic of object properties in JavaScript. It determines whether or not an object property can be enumerated via the for/in loop. You’ll learn more about enumerability in Object Internals and Data Structures

When you create an object with an object initializer or a constructor function all properties are enumerable.

Object.assign Alternatives for ES5 JavaScript-mancers

This article has relied heavily in the use of Object.assign, a new method in ES6 that lets you extend an target object with many other objects. Does that mean that you cannot use mixins and object composition if you are not using ES6? No! You can definitely use object composition and mixins if you haven’t made the jump to ES6 yet.

Chances are that you already are using a library that offers a similar functionality to Object.assign. For instance, jQuery has the $.extend method, underscore the _.extend and _.assign methods and so does lodash:

  • jQuery extend ($.extend): Copies all properties even from prototypes. It can perform deep-copy by using a flag (the source objects is traversed recursively and copied over the target object, this avoids coupling source and target objects)
  • underscore extend (_.extend): Copies all enumerable properties even from prototypes
  • underscore assign (_.assign): Copies only own enumerable properties (not properties inherited from prototypes)
  • lodash extend and assign (_.extend, _.assign): Copies only own enumerable properties

If you are not using any of these libraries don’t worry, you can also implement your own version of Object.assign using this code example below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function assign(){
  var args = Array.prototype.slice.call(arguments, 0),
      target = args[0],
      sources = args.slice(1);

  return sources.reduceRight(assignObject, target);

  function assignObject(target, source){
    for (var prop in source)
      if (source.hasOwnProperty(prop))
        target[prop] = source[prop];

    return target;
  }
}

Which works just like Object.assign:

1
2
3
4
5
6
let thor = new Warrior('Thor', 2000);
assign(thor, canCastSpells);
thor.castsSpell(lightningSpell, orc);
// => Thor casts lightning spell on orc
// => A bolt of lightning electrifies orc (-10hp)
// poor orc

If you are not familiar with the reduceRight function don’t worry, we will take a look at this method and others in the functional programming section of the series. The only thing you need to know right now is that it traverses the sources array from right to left, it applies the assignObject function to each item within the array, accumulates the results in the target object which is fed back into the assignObject function for the next item.

Concluding

Developing software is a complex trade. We try to model the world around us by creating abstractions and simplifications in which we only use the details that we need to solve the problem at hand. Object-Oriented Programming attempts to help reduce the complexities of developing software by defining well defined classes that represent simplified versions of real world entities. However, using classes result in rigid taxonomies that aren’t well suited to absorbing change.

Class-free object oriented programming appears as an alternative solution to classic OOP that is more adaptable to change and more flexible. An example of class-free OOP is object composability through mixins. Mixins are objects or functions that encapsulate a reusable piece of functionality or behavior. You can apply these mixins to your domain model objects by using Object.assign and what is known as concatenative inheritance (the second type of prototypical inheritance we discussed in earlier articles in this series).

You can represent mixins as objects or functions. Function mixins are better than object mixins because they support data privacy and prevent coupling via shared references. Both types of mixins have problems with name collisions because object composition consists in copying properties from one object to the next.

Object.assign is a new Object method in ES6 that lets you copy enumerable own properties from one or several source objects into a target object. If you are not using ES6, you can use alternatives from popular JavaScript libraries like jQuery or underscore, or write your own implementation of Object.assign.

In the next articles in the series we will look at other interesting approaches to achieving class-free object oriented programming: traits and stamps.

P.S. Last week Justin Fagnani wrote a super interesting article about implementing “Real” mixins with ES6 classes. Check it out if you want to learn yet another technique with Mixins!

Interested In Learning More JavaScript? Buy the Book!

Are you a C# Developer interested in learning JavaScript? Then take a look at the JavaScript-mancy book, a complete compendium of JavaScript for C# developers.

a JavaScriptmancy sample cover

More Articles in These Series


  1. Check this excellent talk at nordic.js regarding this very topic http://bit.ly/douglas-crockford-nordicjs

  2. Domain Driven Design (http://bit.ly/wiki-ddd)

Comments