Programming on a Jerker PART 2: Crockford’s New Object Oriented JavaScript Approach or: How I learned to stop using prototypes and learned to love closure

Warning: this is a post entirely for those Jerkers, I mean “Jerks”, who program JavaScript at their Ikea desks.

The reason that Douglas Crockford’s new Classless JavaScript Objects approach is such a “Good Part” is that it “Avoids the general problem we have in inheritance, that you end up inheriting everything in the past history of an object or a class, this case I only inherit the stuff the I actually need. ” We also avoid the quagmire of building brittle and hard to change classification taxonomies before any reasonable amount of knowledge of the project domain has been obtained. Prototypal inheritance does save memory, but when my cheap android phone has 16gb, it is time to put to bed the old assembly language habit of saving bytes.

Below, version 2 of the evolving example program shows how parent and child objects share functions and variables via the members JSON object. The function Snake.movesBy() is passed into the Animal constructor, and the variable inheritance_type_list is added to members inside newAnimal() so that ‘Snake’ can be added to the front of the list in the Snake constructor.

console.clear();
function privitizeNewVariables(specs) {
if (!specs.is_private) {
var members = Object.assign({}, specs);
members.is_private = true;
return members;
}
return specs;
}

function newAnimal(specs) {
var members = privitizeNewVariables(specs);
members.inheritance_type_list = ['Animal'];
whenInDanger = function () {
try{
console.log('When in danger ', members.common_name);
members.movesBy();
}catch (e){
console.log('Error - whenInDanger() has no movesBy()');
}
};
var isA = function(object_type){
if (members.inheritance_type_list.indexOf(object_type)>-1) {
console.log(members.common_name, 'is a', object_type);
}else{
console.log(members.common_name, 'is not a', object_type);
}
}
return Object.freeze({
whenInDanger: whenInDanger,
isA: isA
});
}
function newSnake(specs){
var members = privitizeNewVariables(specs);
members.movesBy = function () {
console.log('Moves By: slithering');
};
colorScheme = function () {
console.log('Color scheme :', members.color_scheme);
};
aPrivateFunction = function (){
console.log('I only exist inside a Snake object');
}
var an_animal = newAnimal(members);
members.inheritance_type_list.unshift('Snake');
return Object.freeze({
whenInDanger: an_animal.whenInDanger,
isA: an_animal.isA,
movesBy: members.movesBy,
colorScheme: colorScheme
});
}
var animal_specs = {common_name: 'Alf the animal'};
var an_animal = newAnimal(animal_specs);
animal_specs.common_name = "does not change Alf's common_name";
an_animal.whenInDanger();
console.log(an_animal);
console.log('-');
var snake_specs = {common_name: 'Snorky the snake', color_scheme:'yellow'};
var a_snake = newSnake(snake_specs);
a_snake.whenInDanger();
console.log('-');
a_snake.colorScheme();
a_snake.isA('Animal');
a_snake.isA('Snake');
a_snake.isA('Bear');

Here is the console output of the above:

When in danger Alf the animal
Error - whenInDanger() has no movesBy()
Object { whenInDanger=function(), isA=function()}
-
When in danger Snorky the snake
Moves By: slithering
-
Color scheme : yellow
Snorky the snake is a Animal
Snorky the snake is a Snake
Snorky the snake is not a Bear

Explanations:

return Object.freeze({
whenInDanger: whenInDanger,
isA: isA
});

When the Animal constructor returns a “frozen” object, it creates an un-corruptible object that closes over its variables/functions hiding everything except what we declare. The only publicly visible elements of Animal objects can be seen with console.log(an_animal) which only shows the 2 functions of whenInDanger() and isA(). Complexity, “Let It Go”.


members.movesBy = function () { ...
colorScheme = function () { ... 
aPrivateFunction = function (){ ...

return Object.freeze({
whenInDanger: an_animal.whenInDanger,
isA: an_animal.isA,
movesBy: members.movesBy,
colorScheme: colorScheme
});

The a_snake.aPrivateFunction() only exists inside a Snake object as can be seen by trying a_snake.aPrivateFunction(). This is because aPrivateFunction() is not listed in the Object.freeze() for the public nor added to members for the Animal constructor. Note that the function Snake.movesBy() was added to members to enable Animal objects to use the function. Because Snake.colorScheme() was not added to members, the function cannot be seen be any Animal objects, but it is publicly available.

var isA = function(object_type){
if (members.inheritance_type_list.indexOf(object_type)>-1) {
console.log(members.common_name, 'is a', object_type);
}else{
console.log(members.common_name, 'is not a', object_type);
}
}

The isA() function uses the inheritance_type_list variable to keep track of what types an object belongs to. There is no public access to the inheritance_type_list variable because “Accessors Are Evil!” Note that both Snake and Animal have access to inheritance_type_list because it is attached to the members variable.

whenInDanger = function () {
try{
console.log('When in danger ', members.common_name);
members.movesBy();
}catch (e){
console.log('Error - whenInDanger() has no movesBy()');
}
};

The Animal(parent) function whenInDanger() calls the Snake(child) function movesBy(). WhenInDanger() can call movesBy() since the function is contained in the shared members variable. You can implement something like abstract functions in a similar manner.

function privitizeNewVariables(specs) {
if (!specs.is_private) {
var members = Object.assign({}, specs);
members.is_private = true;
return members;
}
return specs;
}

The function privitizeNewVariables() copies constructor specifications into the members variable the first time it is encountered when building an object in the constructor hierarchy. This ensures that any later changes to the original specs variable do not magically affect the objects built with it. The statement

animal_specs.common_name = "does not change Alf's common_name";

Does not pertain to the Alf the Animal because a copy, not a reference, was made inside the constructor.

Part  1  2  3  4  5  6  7

Leave a Reply

Your email address will not be published.