Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constructor pattern by Douglas Crockford

Recently I watched a one of a talks by Douglas Crockford (his talks fascinate me, but always leave me confused). He gave an example of a constructor, but I don't quite understand how would I use it in practice:

function constructor(spec) {
  var that = other_constructor(spec),
    member,
    method = function () {
      //spec , member, method
    };

  that.method = method;
  return that;      
}

Maybe some one could give me a simple working example based on this pattern?

like image 286
antanas_sepikas Avatar asked Dec 02 '14 19:12

antanas_sepikas


People also ask

What is constructor pattern?

In classical object-oriented programming languages, a constructor is a special method used to initialize a newly created object once memory has been allocated for it. In JavaScript, as almost everything is an object, we're most often interested in object constructors.

What is a constructor in JavaScript?

A constructor is a special function that creates and initializes an object instance of a class. In JavaScript, a constructor gets called when an object is created using the new keyword. The purpose of a constructor is to create a new object and set values for any existing object properties.


3 Answers

This is Douglas Crockford original source as it appears in his slides:

function constructor(spec) {
  let {member} = spec,
      {other} = other_constructor(spec),
      method = function () {
        // member, other, method, spec
      };
  return Object.freeze({
    method,
    other
  });
}

The following example is a more concrete version of Douglas Crockford's Object creation pattern 2014.

Douglas Crockford makes heavy use of ECMAScript 6 features like destructuring etc.!!

Start the code in node.js with following options (enable ES6):

node --harmony --harmony_destructuring demo.js

demo.js

// Douglas Crockford 2014 Object Creation
(function() {
  'use strict';

  function adress(spec) {

    let {
      street, city
    } = spec,
    logAdress = function() {
      console.log('Adress:', street, city);
    };
    return Object.freeze({
      logAdress
    });
  };

  function person(spec) {

    let {
      preName,
      name
    } = spec, {
      logAdress
    } = adress(spec),
      logPerson = function() {
        // member, other, method, spec
        console.log('Name: ', preName, name);
        logAdress();
      };
    return Object.freeze({
      logPerson,
      logAdress
    });
  };


  let myPerson = person({
    preName: 'Mike',
    name: 'Douglas',
    street: 'Newstreet',
    city: 'London'
  });

  myPerson.logPerson();
})();

According to Douglas Crockford’s talk, he avoids the use of:

  • new
  • Object.create
  • this !!!

Watch the original Crockford video: https://www.youtube.com/watch?v=PSGEjv3Tqo0

A good explanation for the Crockford Douglas Object Creation Pattern 2014 is this blog: https://weblogs.asp.net/bleroy/crockford%E2%80%99s-2014-object-creation-pattern

like image 88
AndreasE Avatar answered Oct 16 '22 09:10

AndreasE


This is an example of using another constructor inside a factory function to return an object. In this case, other_constructor is the constructor function, which is creating an object of the type other_constructor (ideally in practice this would be capitalized). That object is stored in that. In this factory function, method is a defined function which is added to that to extend the object's functionality in some way.

The difference between constructor and factory functions is that a factory function is just a normal function that returns an object, whereas a constructor function has this pointing to the new object, and usually has to be called with the new keyword preceding it.

A typical constructor function:

function Dog(breed, height, name){
  this.breed = breed;
  this.animalType = "dog";
  this.height = height;
  this.name = name;
  // calling `return` isn't necessary here
}

And it's usage:

var lab = new Dog("labrador", 100, "Sugar"); // `new` is necessary (usually)
console.log(lab.animalType); // prints out "dog"
console.log(lab.height); // prints out 100

A typical factory function:

function createDog(breed, height, name){
  var dog = {
    breed: breed,
    height: height,
    animalType: "dog",
    name: name
  };
  return dog; 
  // `return` is necessary here, because `this` refers to the 
  // outer scope `this`, not the new object
}

And its usage:

var lab = createDog("labrador", 100, "Sugar"); // notice no need for `new`
console.log(lab.animalType); // prints out "dog"
console.log(lab.height); // prints out 100

A good explanation of the difference between them and the different use cases of each is at Eric Elliot's blog

like image 20
PitaJ Avatar answered Oct 16 '22 11:10

PitaJ


Vanilla JavaScript examples of Douglas Crockford's new constructor pattern with explanations:

  • general OOP example
  • multiple inheritence example

console.clear();
var fauna = (function (){
  privitizeNewVariables=function (specs) {
    if (!specs.is_private) {
      var members = Object.assign({}, specs);
      members.is_private = true;
      return members;
    }
    return specs;
  },
  newAnimal=function (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
    });
  },
  newSnake=function (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
    });
  };
  return {
    newAnimal:newAnimal,
    newSnake: newSnake
  }
})();
var animal_specs = {common_name: 'Alf the animal'};
var an_animal = fauna.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 = fauna.newSnake(snake_specs);
a_snake.whenInDanger();
console.log('-');
a_snake.colorScheme();
a_snake.isA('Animal');
a_snake.isA('Snake');
a_snake.isA('Bear');
console.log('-');
console.log(fauna);
like image 20
Steen Hansen Avatar answered Oct 16 '22 09:10

Steen Hansen