Not too long ago in the era of Javascript ES5, I played around a bit creating a very simple Javascript library called Species. While developing this library I encountered some weirdness and, in the process, nearly broke my mind.

Let me tell you about it.

The aim of Species

In this phase of my life I obsessed with anthropomorphizing javascript objects, and somehow doing away with the this keyword.

I also wanted to mimic classification and inheritance as it was understood in classical biology. Different kinds of objects are analogous to different kinds of creature, I thought, and just as one creature can inherit it’s characteristics and behaviour from it’s parent, javascript objects would, in my new library, be able to inherit from a parent.

This library would follow a once popular Javascript Design Pattern: adapting prototypical inheritance to implement class inheritance.

Exploring Species

Species has only one function: the Species contructor function, which accepts a blueprint object and returns a new constructor function that creates objects with the characteristics defined in the blueprint.

var pigBlueprint = {
  tail: "curly"
}

var Pig = new Species(pigBlueprint)
var pepper = new Pig()

console.log(pepper.tail) // => "curly"

Not very interesting. Let’s see what else we can do.

Inheritance

By specifying an ancestorSpecies in the bluesprint that you pass into the Species constructor function, you can create a Species that inherits characteristics from the specified ancestorSpecies.

Inheritance from a plain old javascript object.

var Wolf = { wild : true };
Wolf.prototype = {};
Wolf.prototype.communicate = function() { return "woof" };

var Dog = Species({ ancestorSpecies : Wolf });

var fido = new Dog()

console.log(fido instanceof Dog) // => true
console.log(fido.ancestorSpecies) // => the Wolf object
console.log(fido.communicate()) // => "woof"
console.log(fido.wild) // => undefined

Inheritance from another Species

Specifying a bio function in the bluesprint object passed to the constructor allows you to define attributes that are not shared among instances of that species. You can also return an anonymous inner function in the bio function that will be executed upon construction.

var Vulcan = new Species({
  bio: function() {
    this.haircut = "bad";
    return function() {
      this.ears = "pointy";
      this.emotional = false;
    };
  },
  homeworld: "Vulcan"
});

var Romulan = Species({
  ancestorSpecies : Vulcan,
  bio: function() {
    return function() {
      this.emotional = true;
    };
  },
  homeworld: "Romulus"
});

var spock = new Vulcan();
var tuvok = new Vulcan();
var remus = new Romulan();

console.log(remus instanceof Romulan) // => true
console.log(remus instanceof Vulcan) // => true

Let’s see what attributes and behaviour are shared with other instances of the same species.

console.log(remus.ears) // => "pointy"
console.log(spock.ears) // => "pointy"
console.log(tuvok.ears) // => "pointy"
remus.ears = "burned"
tuvok.ears = "chopped off"
console.log(remus.ears) // => "burned"
console.log(spock.ears) // => still "pointy"
console.log(tuvok.ears) // => "chopped off"

console.log(spock.haircut) // => "bad"
console.log(tuvok.haircut) // => "bad"
console.log(remus.haircut) // => "bad"
spock.haircut = "hipster trendy"
console.log(spock.haircut) // => now "hipster trendy"
console.log(tuvok.haircut) // => still "bad"
console.log(remus.haircut) // => still "bad"


console.log(remus.emotional) // => true
console.log(spock.emotional) // => false
console.log(tuvok.emotional) // => false

Attributes defined outside the bio function are added to the prototypes of the object instances and are shared by all instances of the same Species.

spock.__proto__.homeworld = "Earth"
console.log(spock.homeworld) // => now "Earth"
console.log(tuvok.homeworld) // => "Earth" - all vulcans have the same homeworld
console.log(remus.homeworld) // => still "Romulus"

Using personal pronoun syntax in place of ‘this’

I mentioned earlier that I was trying to do away with the this keyword in Javascript. I wasn’t kidding.

In the bio function, list personal pronouns you would like to use (or any words for that matter) in place of the this keyword, and they will be aliased for you and available to use inside the bio function.

var Bird = new Species({
  // In the bio function parameters, specify the list of...
  // ...personal pronouns you wish to use. Can be anything.
  bio : function(my, I) {
    // Then, inside bio function, use personal pronouns instead of `this`.
    I.sayName = function() {
      return "My name is " +  my.name;
    }
    return function(name) {
      my.name = name;
    }
  }
});

var woody = new Bird("woody the woodpecker");
console.log(woody.sayName()) // => "My name is woody the woodpecker"

var Duck = new Species({
  ancestorSpecies : Bird,
  // In the bio function parameters, specify the list of...
  // ...personal pronouns you wish to use. Can be anything.
  bio : function(my, I) {
    // Then, inside bio function, use personal pronouns instead of `this`.
    I.quack = function(words) {
      return "quack! " + words + " quack!"
    }
    return function(name, hat) {
      // Assignment of name attribute happens in parent so no need to do it here
      my.hat = hat;
    }
  }
});

var donald = new Duck("donald duck", "sailor");
console.log(donald.sayName()) // => "My name is donald duck"
console.log(donald.quack("I")) // => "quack! I quack!"
console.log(donald.hat) // => "sailor"

Reality unwinding

So far so good. However, at about this point, things start to become weird.

var Human = new Species()
var bob = new Human()

console.log(Human.prototype.constructor.name) // => "God" ??????
console.log(bob.constructor.name) // => "God"

God eh? Yep.

It seemed appropriate for the object creating new species objects to be named God. What could be the harm? 1.

1. Little did I know…

Let’s examine the nature of this “God” object.

var God = bob.constructor

console.log(God) // => function God() { }

console.log(God.prototype.name) // => "God"
console.log(God.prototype === God) // => God

Interesting: God is a function and is his own prototype. So God is created in his own image. Neat.

Does God have the ability to create life?

var Flower = God.createSpecies({
  bio : function(Iam, my) {
    Iam.pretty = true
    return function(color) {
      my.color = color;
    }
  }
})

var tulip = God.bringToLife(null, Flower, "red")

console.log(tulip.color) // => "red"
console.log(tulip.pretty) // => true
console.log(tulip instanceof Flower) // => true

Yes, God can create life.

So what’s the meaning of life, God?

console.log(God.meaningOfLife) // => 42

Oh very witty. 2

2. You knew that was coming, right?

I wonder whether each Species has its own separate god.

var fidoGod = fido.constructor
var spockGod = spock.constructor
var tuvokGod = tuvok.constructor
var remusGod = tuvok.constructor

console.log(God === fido.constructor) // => true
console.log(God === spock.constructor) // => true
console.log(God === tuvok.constructor) // => true
console.log(God === remus.constructor) // => true

Woh!. There is only one God. Deep.

So God created all the creatures (objects) of each Species.

But, wait a moment…

I’m the developer, aren’t I?

Didn’t I create these objects?

Hmm…

…Oh of course!

Ha ha [nervous laughs] How silly!

God created all the Species and creatures (i.e. instances of Species), and I created God.

So man created God afterall. Ha ha…

…ha…

But, then…

console.log(God.constructor.name) // => "God"

…why does it say God created God?

Wait…

What?!!

[feels like falling]

Wwwwwwwwwhhhhhhhhhhhhhhhhhaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!!!!!

[takes a breath]

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaagggggggghhhhhhhhhhhhhhhhhhhhhh

[sometime later]

I’m ok. I’m ok.

Wwwwwwaaaaaaaaaaaaaaaaaaaaggggggggghhhhhhh!!!!!!!



Programming is fun

Don’t worry. I’m alright now.

Programming is so powerful, you can literally create your own worlds. Mucking about with code is really fun and can teach you deep lessons about the language you’re using. Why not play around with some ideas yourself? You may be surprised.

The code for the Species library

If anyone is interested in using this odd and totally pointless little library (please don’t) or just wants to see the code (why would you?) then here it is:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
var Species = (function() {

  var grantKnowledgeOfCreatorTo = function(species) {
    species.prototype.constructor = God;
  };

  var copyAttributes = function(attrSource, attrDestination){
    for (var attribute in attrSource) {
      if (attrSource.hasOwnProperty(attribute)) {
        attrDestination.prototype[attribute] = attrSource[attribute];
      }
    }
  };

  var applyDNA =  function(ancestorSpecies, childSpecies) {
    var DNA = function DNA() {};
    DNA.prototype = ancestorSpecies.prototype;
    childSpecies.prototype = new DNA();
    childSpecies.ancestorSpecies = ancestorSpecies.prototype;
  };

  var generateIdentitiesFor = function(self, identityCount) {
    return Array.apply(null, new Array(identityCount)).map(Object.prototype.valueOf, self);
  };

  var consciousness = function(speciesBio, attributes) {
    var numberOfIdentities = speciesBio.length;
    var selfAwareness = generateIdentitiesFor(this, numberOfIdentities);
    var selfAwareBeing = [this].concat(selfAwareness);
    var powerOfLife = speciesBio.apply(this, selfAwareBeing);
    if (typeof powerOfLife === 'function'){
      powerOfLife.apply(this, attributes);
    }
  };

  var God = function God() { };
  God.meaningOfLife = 42;
  God.prototype = God;
  God.prototype.constructor = God;

  God.bringToLife = function(newBeing, speciesType) {
    var attributes = Array.prototype.slice.call(arguments, 2);
    var being = newBeing || new speciesType();

    if (speciesType.ancestorSpecies && speciesType.ancestorSpecies.hasOwnProperty("bio")) {
      var ancestorBio = speciesType.ancestorSpecies.bio || function(){};
      consciousness.call(being, ancestorBio, attributes);
    }

    if (speciesType.prototype.hasOwnProperty("bio")) {
      var speciesBio = speciesType.prototype.bio || function(){};
      consciousness.call(being, speciesBio, attributes);
    }

    return being;
  };

  God.createSpecies = function(blueprint, constructorFunction) {
    var species = constructorFunction || function Species() {
      God.bringToLife.apply(this, [this, Species].concat(Array.prototype.slice.call(arguments)));
    };
    var ancestorSpecies = (blueprint && blueprint.ancestorSpecies) ? blueprint.ancestorSpecies : Object;
    applyDNA(ancestorSpecies, species);
    copyAttributes(blueprint, species);
    grantKnowledgeOfCreatorTo(species);
    return species;
  };

  return function(blueprint) {
    return God.createSpecies(blueprint);
  };

})();