My Javascript Existential Crisis
Tweet this postNot 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.
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.
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.
Let’s see what attributes and behaviour are shared with other instances of the same species.
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.
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.
Reality unwinding
So far so good. However, at about this point, things start to become weird.
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.
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?
Yes, God can create life.
So what’s the meaning of life, God?
Oh very witty. 2
2. You knew that was coming, right?
I wonder whether each Species has its own separate god.
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…
…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);
};
})();