Programming on a Jerker PART 3: Non-Prototype Based JavaScript OOP or: How I decide what gets called when, with “My” constructors

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

Back when I was young I really liked assembly language programming; it was so fast and cool. Then one day I thought:

Why am I writing a whole page of instructions just to say Hello World? And Task State Segments look really boring and hard. This is too complicated and not fun anymore.

That was the day I switched to C++ and learned about OOP. A little later I ran into PHP. I really liked not having to cast the string ‘1’ to the integer 1; so much faster. No DLL Hell, no installation disks, and PHP programs run on somebody else’s computer!

Now I am having the same experience with Crockford’s new object oriented JavaScript approach with respect to OOP. Back in school they would always make us draw these huge UML diagrams, which were always wrong, and then code to that specification. Woe be unto you when it needed changing, everything was inter-related. With classless JavaScript objects I don’t have to figure out a classification taxonomy immediately, and then hope I am right, I can just start coding in an easy to understand manner. Odd edge cases are also easier to handle.

The example program below shows how to easily use defaults, overrides, and interfaces. Again, all the internal properties and functions are hung on the hidden member variable:

console.clear();
function checkNewInterface(members, must_have){
  var object_name=members.inheritance_type_list[0];
  for (var property_name in must_have) {
    var must_be_member = must_have[property_name];
    if(!members.hasOwnProperty(must_be_member)) {
      console.log(object_name, "is missing", must_be_member);
    }
  }
}
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'];
  checkNewInterface(members, ['common_name', 'movesBy']);
  var bodyDescription = function () {
    var body_description = '';
    if (members.color_scheme) {
      body_description += members.color_scheme + ',';
    }
    if (members.body_covering) {
      body_description += members.body_covering + ',';
    }
    if (''===body_description){
      body_description = 'none';
    }
    return body_description;
  };
  if (!members.hasOwnProperty('makeOffspring')) {
    members.makeOffspring = function () {
      console.log('Offspring by: asexually')
    }
  }
  if (!members.hasOwnProperty('movesBy')) {
    members.movesBy = function () {
      console.log('Moves by: wriggling');
    };
  }
  return Object.freeze({
    bodyDescription: bodyDescription,
    movesBy: members.movesBy,
    makeOffspring: members.makeOffspring
  });
}
function newSnake(specs){
  var members = privitizeNewVariables(specs);
  members.movesBy = function () {
    console.log('Moves By: slithering');                       
  }
  members.body_covering ='scales';
  if (!members.hasOwnProperty('makeOffspring')) {
    members.makeOffspring = function () {
      console.log('Offspring by: laying eggs')
    }
  }
  if (!members.hasOwnProperty('color_scheme')) {
    members.color_scheme ='brown';
  }
  var an_animal = newAnimal(members);
  var bodyDescription = function () {
    console.log('Animal Body Description :', an_animal.bodyDescription());  
    console.log(' with Fangs');
  };
  members.inheritance_type_list.unshift('Snake');
  checkNewInterface(members, ['common_name']);
  return Object.freeze({
    bodyDescription: bodyDescription,
    movesBy: members.movesBy,
    makeOffspring: members.makeOffspring,
    DANGEROUS_IF: "Red on yellow, deadly fellow"
  });
}
var snorky_specs = {common_name: 'Snorky the snake',
    movesBy: function (){console.log('Moves By: flying is ignored')},
    color_scheme:'blue'};
var snorky_snake = newSnake(snorky_specs);
snorky_snake.movesBy();
snorky_snake.bodyDescription();
console.log('-');
var watersnake_specs = {common_name: 'Northern the Watersnake',
    makeOffspring: function (){console.log('Offspring by: live-birthing')}  };
var northern_watersnake = newSnake(watersnake_specs);
northern_watersnake.makeOffspring();
northern_watersnake.bodyDescription();
console.log('-');
console.log("A snake is dangerous if ", northern_watersnake.DANGEROUS_IF);
var un_named_snake_err = newSnake({});

Here is the console output of the above:

Moves By: slithering
Animal Body Description : blue,scales,
 with Fangs
-
Offspring by: live-birthing
Animal Body Description : brown,scales,
 with Fangs
-
A snake is dangerous if Red on yellow, deadly fellow
Animal is missing common_name
Snake is missing common_name

Explanations:

function checkNewInterface(members, must_have){
  var object_name=members.inheritance_type_list[0];
  for (var property_name in must_have) {
    var must_be_member = must_have[property_name];
    if(!members.hasOwnProperty(must_be_member)) {
      console.log(object_name, "is missing", must_be_member);
    }
  }
}

We can ensure that constructors have a certain set of required values and/or functions in their specifications. The checkNewInterface() function complains if something is missing. For example, the below function call will complain if the variable common_name and/or the function movesBy() do not exist. This occurs above with the un_named_snake_err because it has no common_name.

checkNewInterface(members, ['common_name', 'movesBy']);
members.body_covering ='scales';

Every Snake will have scales because of the above statement, even if another value is in the specifications.

members.movesBy = function () {
 console.log('Moves By: slithering'); 
}

Every Snake will slither, regardless of any other function specified.

 if (!members.hasOwnProperty('color_scheme')) {
   members.color_scheme ='brown';
 }

If any Snake has no color in its specification, it then gets the default ‘brown’ color. Snorky the Snake, however stays ‘blue’.

 if (!members.hasOwnProperty('makeOffspring')) {
   members.makeOffspring = function () {
     console.log('Offspring by: laying eggs')
   }
 }

Northern the Watersnake, uses ‘live-birth’ so the default method of laying eggs is ignored.

var bodyDescription = function () {
    var body_description = '';
    if (members.color_scheme) {
      body_description += members.color_scheme + ',';
    }
    if (members.body_covering) {
      body_description += members.body_covering + ',';
    }
    if (''===body_description){
      body_description = 'none';
    }
    return body_description;
  };

The above function returns a description of the bodies of the animals if they have color and/or a body covering.

DANGEROUS_IF: “Red on yellow, deadly fellow”

You can even have constants like above.

Part 1 2 3 4 5 6 7

Leave a Reply

Your email address will not be published.