5

I've been traveling deeper into the JS world and came across 3 different ways I could develop the frontend cart of a website:

Constructor with Prototype Functions

var cart = function(){
    this.items = {}
}

cart.prototype.increaseItemQty = function(partNumber){
    if(this.items[partNumber]){
        this.items[partNumber].qty += 1;
    } else {
        this.items[partNumber] = {
            qty : 1
        };
    }
}

cart = new cart();

Methods Within the Constructor

var cart2 = function(){
    this.items = {};
    this.increaseItemQty = function (partNumber) {
        if(this.items[partNumber]){
            this.items[partNumber].qty += 1;
        } else {
            this.items[partNumber] = {
                qty : 1
            };
        }
    }
}

cart2 = new cart2();

Object Methods

var cart3 = {
    items : {},
    increaseItemQty : function(partNumber){
        if(this.items[partNumber]){
            this.items[partNumber].qty += 1;
        } else {
            this.items[partNumber] = {
                qty : 1
            };
        }
    }
};

If I know that there will only be one instance of the cart, then should I just use the Object Method method? Is there any reason why I should still use a constructor?

Jared Dunham
  • 1,467
  • 2
  • 17
  • 29

4 Answers4

3

Yes, there is a (minor) reason you shouldn't use a constructor: the instance will hold a reference to the constructor function via instance.[[Prototype]].constructor, and thus it won't be garbage collected. It will waste memory uselessly because you won't instantiate it again.

var cart = new function(){ /* ... */ };
console.log(cart.constructor); // Won't be garbage-collected

If you prefer a function approach you can still do something like the following. This way you can also have private variables.

var cart = function() {
  // You can add private variables here
  this.items = {};
  this.increaseItemQty = function(partNumber) {
    if(this.items[partNumber]) {
      this.items[partNumber].qty += 1;
    } else {
      this.items[partNumber] = {
        qty : 1
      };
    }
  };
  return this;
}.call({});
cart.increaseItemQty(0);
console.log(cart.items);
Oriol
  • 274,082
  • 63
  • 437
  • 513
2

You should use constructor functions if you think that you'll need to initialize your carts with some defaults or do some initialization stuff that should be mandatorily performed prior to use the whole object.

In the other hand, maybe you're missing Object.create. You don't need constructor functions to implement a prototype chain:

var A = {
    doStuff() {
        console.log("I did some stuff");
    }
};

var B = Object.create(A);
B.moreStuff = function() {
   console.log("I did even more stuff");
};

// A new object where its prototype is B
var someB = Object.create(B);
b.doStuff(); // ...from A
b.moreStuff(); // ...from B
Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
  • Why `B.prototype = {...`? That won't give `b` any methods without having to do `b.prototype.moreStuff()`. You can assign properties in the second argument to `Object.create`. –  Dec 18 '16 at 21:37
  • @squint you can do that, but you'll need to use property descriptors, which is too verbose for simpler cases. – Matías Fidemraizer Dec 18 '16 at 21:40
  • Yes, property descriptors are verbose, but one way or another you'll need to change how you provide `moreStuff` in order to be able to do `b.moreStuff()` instead of `b.prototype.moreStuff()`. –  Dec 18 '16 at 21:41
  • 1
    @squint Fixed, you were right. My mind got corrupted :D – Matías Fidemraizer Dec 18 '16 at 21:44
1

Difference between cases 1 and 2: in case 1, you assign methods to all instances of cart class or its child classes, in case 2 only to instances of cart.

Difference between cases 2 and 3: in case 3, furthermore, you create just one instance just by using object initializer expression and assign some methods as its properties.

Now, if to answer your question, if you have only one instance, theoretically it will be enough for you to add method as its property like in case 3. Or even write procedural-style functions.

But in terms of extensibility and reusability of code, the best practise is utilize methods by creating classes. Classes in javascript are sets of objects with the same prototype. So the best is to add methods as properties of the prototype object like in case 1. If you will have only one instance - there will be no difference to you. But since cart should be pretty heavy and complex class, whenever requirements to your cart change, case 1 will be the most convenient.

pttsky
  • 737
  • 5
  • 15
  • I would love to use classes, but this would have to work in IE and Android web browsers. – Jared Dunham Dec 18 '16 at 21:41
  • @JaredDunham I mean not using ES6 `class` word. I mean classes as objects with the same value of `prototype` property. That is supported everywhere. You should look at some [examples of prototype inheritance in js](http://stackoverflow.com/questions/2064731/good-example-of-javascripts-prototype-based-inheritance) – pttsky Dec 18 '16 at 21:46
1

To create an object, basically it can be created using the constructor 'function' or simply with the object notation '{ }' which both are exactly doing the same in your case above.

The main advantage with the constructor pattern is, it will be more useful if you need to deal with the arguments when you create an instance as below.

function Car(model) {
   this.model = model;
}

Or if you need to use private variables and methods as below.

function Car() {
   // private variable
   var modelYear = 1990;
   // private method
   var isEligibleToInsurance = function() {
       return this.modelYear > modelYear;
   };

  this.isLatestModel = function() {
      return isEligibleToInsurance();
  };
}

This way you can have a private method and use the same inside the public methods. When you use object notation, you can't have a common private method used across all the public methods and when you use 'prototype', you can't use the private methods declared inside the constructor.

Note: When you name the class, you can follow pascal case to name it as "Car" instead of "car" and create instances with camel case as "var car = new Car();"

Aruna
  • 11,959
  • 3
  • 28
  • 42