[javascript] JavaScript pattern for multiple constructors

I need different constructors for my instances. What is a common pattern for that?

This question is related to javascript design-patterns oop

The answer is


How do you find this one?

function Foobar(foobar) {
    this.foobar = foobar;
}

Foobar.prototype = {
    foobar: null
};

Foobar.fromComponents = function(foo, bar) {
    var foobar = foo + bar;
    return new Foobar(foobar);
};

//usage: the following two lines give the same result
var x = Foobar.fromComponents('Abc', 'Cde');
var y = new Foobar('AbcDef')

export default class Order {

    static fromCart(cart) {
        var newOrder = new Order();
        newOrder.items = cart.items;
        newOrder.sum = cart.sum;

        return newOrder;
    }

    static fromOrder(id, order) {
        var newOrder = new Order();
        newOrder.id = id;
        newOrder.items = order.items;
        newOrder.sum = order.sum;

        return newOrder;
    }
}

Useges:

  var newOrder = Order.fromCart(cart)
  var newOrder = Order.fromOrder(id, oldOrder)

Sometimes, default values for parameters is enough for multiple constructors. And when that doesn't suffice, I try to wrap most of the constructor functionality into an init(other-params) function that is called afterwards. Also consider using the factory concept to make an object that can effectively create the other objects you want.

http://en.wikipedia.org/w/index.php?title=Factory_method_pattern&oldid=363482142#Javascript


I believe there are two answers. One using 'pure' Javascript with IIFE function to hide its auxiliary construction functions. And the other using a NodeJS module to also hide its auxiliary construction functions.

I will show only the example with a NodeJS module.

Class Vector2d.js:



/*

    Implement a class of type Vetor2d with three types of constructors.

*/

// If a constructor function is successfully executed,
// must have its value changed to 'true'.let global_wasExecuted = false;  
global_wasExecuted = false;   

//Tests whether number_value is a numeric type
function isNumber(number_value) {
    
    let hasError = !(typeof number_value === 'number') || !isFinite(number_value);

    if (hasError === false){
        hasError = isNaN(number_value);
    }

    return !hasError;
}

// Object with 'x' and 'y' properties associated with its values.
function vector(x,y){
    return {'x': x, 'y': y};
}

//constructor in case x and y are 'undefined'
function new_vector_zero(x, y){

    if (x === undefined && y === undefined){
        global_wasExecuted = true;
        return new vector(0,0);
    }
}

//constructor in case x and y are numbers
function new_vector_numbers(x, y){

    let x_isNumber = isNumber(x);
    let y_isNumber = isNumber(y);

    if (x_isNumber && y_isNumber){
        global_wasExecuted = true;
        return new vector(x,y);
    }
}

//constructor in case x is an object and y is any
//thing (he is ignored!)
function new_vector_object(x, y){

    let x_ehObject = typeof x === 'object';
    //ignore y type

    if (x_ehObject){

        //assigns the object only for clarity of code
        let x_object = x;

        //tests whether x_object has the properties 'x' and 'y'
        if ('x' in x_object && 'y' in x_object){

            global_wasExecuted = true;

            /*
            we only know that x_object has the properties 'x' and 'y',
            now we will test if the property values ??are valid,
            calling the class constructor again.            
            */
            return new Vector2d(x_object.x, x_object.y);
        }

    }
}


//Function that returns an array of constructor functions
function constructors(){
    let c = [];
    c.push(new_vector_zero);
    c.push(new_vector_numbers);
    c.push(new_vector_object);

    /*
        Your imagination is the limit!
        Create as many construction functions as you want.    
    */

    return c;
}

class Vector2d {

    constructor(x, y){

        //returns an array of constructor functions
        let my_constructors = constructors(); 

        global_wasExecuted = false;

        //variable for the return of the 'vector' function
        let new_vector;     

        //traverses the array executing its corresponding constructor function
        for (let index = 0; index < my_constructors.length; index++) {

            //execute a function added by the 'constructors' function
            new_vector = my_constructors[index](x,y);
            
            if (global_wasExecuted) {
            
                this.x = new_vector.x;
                this.y = new_vector.y;

                break; 
            };
        };
    }

    toString(){
        return `(x: ${this.x}, y: ${this.y})`;
    }

}

//Only the 'Vector2d' class will be visible externally
module.exports = Vector2d;  

The useVector2d.js file uses the Vector2d.js module:

const Vector = require('./Vector2d');

let v1 = new Vector({x: 2, y: 3});
console.log(`v1 = ${v1.toString()}`);

let v2 = new Vector(1, 5.2);
console.log(`v2 = ${v2.toString()}`);

let v3 = new Vector();
console.log(`v3 = ${v3.toString()}`);

Terminal output:

v1 = (x: 2, y: 3)
v2 = (x: 1, y: 5.2)
v3 = (x: 0, y: 0)

With this we avoid dirty code (many if's and switch's spread throughout the code), difficult to maintain and test. Each building function knows which conditions to test. Increasing and / or decreasing your building functions is now simple.


Answering because this question is returned first in google but the answers are now outdated.

You can use Destructuring objects as constructor parameters in ES6

Here's the pattern:

You can't have multiple constructors, but you can use destructuring and default values to do what you want.

export class myClass {

  constructor({ myArray = [1, 2, 3], myString = 'Hello World' }) {

    // ..
  }
}

And you can do this if you want to support a 'parameterless' constructor.

export class myClass {

      constructor({myArray = [1, 2, 3], myString = 'Hello World'} = {}) {

        // ..
      }
}

you can use class with static methods that return an instance of that class

    class MyClass {
        constructor(a,b,c,d){
            this.a = a
            this.b = b
            this.c = c
            this.d = d
        }
        static BAndCInstance(b,c){
            return new MyClass(null,b,c)
        }
        static BAndDInstance(b,d){
            return new MyClass(null,b, null,d)
        }
    }

    //new Instance just with a and other is nul this can
    //use for other params that are first in constructor
    const myclass=new MyClass(a)

    //an Instance that has b and c params
    const instanceWithBAndC = MyClass.BAndCInstance(b,c)

    //another example for b and d
    const instanceWithBAndD = MyClass.BAndDInstance(b,d)

with this pattern you can create multi constructor


Didn't feel like doing it by hand as in bobince's answer, so I just completely ripped off jQuery's plugin options pattern.

Here's the constructor:

//default constructor for Preset 'class'
function Preset(params) {
    var properties = $.extend({
        //these are the defaults
        id: null,
        name: null,
        inItems: [],
        outItems: [],
    }, params);

    console.log('Preset instantiated');
    this.id = properties.id;
    this.name = properties.name;
    this.inItems = properties.inItems;
    this.outItems = properties.outItems;
}

Here's different ways of instantiation:

presetNoParams = new Preset(); 
presetEmptyParams = new Preset({});
presetSomeParams = new Preset({id: 666, inItems:['item_1', 'item_2']});
presetAllParams = new Preset({id: 666, name: 'SOpreset', inItems: ['item_1', 'item_2'], outItems: ['item_3', 'item_4']});

And here's what that made:

presetNoParams
Preset {id: null, name: null, inItems: Array[0], outItems: Array[0]}

presetEmptyParams
Preset {id: null, name: null, inItems: Array[0], outItems: Array[0]}

presetSomeParams
Preset {id: 666, name: null, inItems: Array[2], outItems: Array[0]}

presetAllParams
Preset {id: 666, name: "SOpreset", inItems: Array[2], outItems: Array[2]}

Going further with eruciform's answer, you can chain your new call into your init method.

function Foo () {
    this.bar = 'baz';
}

Foo.prototype.init_1 = function (bar) {
    this.bar = bar;
    return this;
};

Foo.prototype.init_2 = function (baz) {
    this.bar = 'something to do with '+baz;
    return this;
};

var a = new Foo().init_1('constructor 1');
var b = new Foo().init_2('constructor 2');

This is the example given for multiple constructors in Programming in HTML5 with JavaScript and CSS3 - Exam Ref.

function Book() {
    //just creates an empty book.
}


function Book(title, length, author) {
    this.title = title;
    this.Length = length;
    this.author = author;
}

Book.prototype = {
    ISBN: "",
    Length: -1,
    genre: "",
    covering: "",
    author: "",
    currentPage: 0,
    title: "",

    flipTo: function FlipToAPage(pNum) {
        this.currentPage = pNum;
    },

    turnPageForward: function turnForward() {
        this.flipTo(this.currentPage++);
    },

    turnPageBackward: function turnBackward() {
        this.flipTo(this.currentPage--);
    }
};

var books = new Array(new Book(), new Book("First Edition", 350, "Random"));

JavaScript doesn't have function overloading, including for methods or constructors.

If you want a function to behave differently depending on the number and types of parameters you pass to it, you'll have to sniff them manually. JavaScript will happily call a function with more or fewer than the declared number of arguments.

function foo(a, b) {
    if (b===undefined) // parameter was omitted in call
        b= 'some default value';

    if (typeof(a)==='string')
        this._constructInSomeWay(a, b);
    else if (a instanceof MyType)
        this._constructInSomeOtherWay(a, b);
}

You can also access arguments as an array-like to get any further arguments passed in.

If you need more complex arguments, it can be a good idea to put some or all of them inside an object lookup:

function bar(argmap) {
    if ('optionalparam' in argmap)
        this._constructInSomeWay(argmap.param, argmap.optionalparam);
    ...
}

bar({param: 1, optionalparam: 2})

Python demonstrates how default and named arguments can be used to cover the most use cases in a more practical and graceful way than function overloading. JavaScript, not so much.


Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to design-patterns

How to implement a simple scenario the OO way Implementing Singleton with an Enum (in Java) What is difference between MVC, MVP & MVVM design pattern in terms of coding c# Best Practices for mapping one object to another REST API Login Pattern When should we use Observer and Observable? How to implement a FSM - Finite State Machine in Java Function in JavaScript that can be called only once Thread Safe C# Singleton Pattern Repository Pattern Step by Step Explanation

Examples related to oop

How to implement a simple scenario the OO way When to use 'raise NotImplementedError'? PHP: cannot declare class because the name is already in use Python class input argument Call an overridden method from super class in typescript Typescript: How to extend two classes? What's the difference between abstraction and encapsulation? An object reference is required to access a non-static member Java Multiple Inheritance Why not inherit from List<T>?