Object guidelines

Javascript is not a class-based language, it is a multi-paradigm programming language that supports object-oriented programming (OOP) based on prototypes.
This gives us several different ways of working with objects.
But in general, the following guidelines should be followed:

"Start with the simplest implementation and move to more complex implementations only as required:  
Functions > objects > factory functions > functional mixins > classes"  
"You should always use the simplest possible abstraction to solve the problem you’re working on. Start with a pure function. If you need an object with persistent state, try a factory function. If you need to build more complex objects, try functional mixins."  

https://medium.com/javascript-scene/functional-mixins-composing-software-ffb66d5e731c

I will try to generate examples and collect all the pros and cons of the different implementations and why we should choose to follow the previous guideline.

Functions

The functions must be pure functions:

  • Each input combination corresponds to exactly one output.
  • They only depend on the input data.
  • They do not alter the input data. (Immutability)

They should not produce any side effects, that is, they should not change the status of the program.

https://codeburst.io/functional-programming-in-javascript-e57e7e28c0e5

Using declarations:

const square = {
  length: 10,
  breadth: 10
};

function getArea(square) {
  return square.length * square.breadth;
}

function printAll(square) {
  console.log(`length: ${square.length}`);
  console.log(`breadth: ${square.breadth}`);
  console.log(`area: ${getArea(square)}`);
}

printAll(square);

Using expressions:

const square = {
  length: 10,
  breadth: 10
};

const getArea = function(square) {
  return square.length * square.breadth;
}

const printAll = function(square) {
  console.log(`length: ${square.length}`);
  console.log(`breadth: ${square.breadth}`);
  console.log(`area: ${getArea(square)}`);
}

printAll(square);

After ES6 we can write them using the arrow functions:

const square = {
  length: 10,
  breadth: 10
};

const getArea = square => {
  return square.length * square.breadth;
};

const printAll = square => {
  console.log(`length: ${square.length}`);
  console.log(`breadth: ${square.breadth}`);
  console.log(`area: ${getArea(square)}`);
};

printAll(square);

Objects

In the following two examples we can see that we can define methods within an object. We may want to do this to encapsulate these functions and make it clear that they belong to the object.

const square = {
  length: 10,
  breadth: 10,

  getArea: function() {
    return this.length * this.breadth;
  }
};

square.printAll = function() {
  console.log(`length: ${this.length}`);
  console.log(`breadth: ${this.breadth}`);
  console.log(`area: ${this.getArea()}`);
};

square.printAll();

Note that one of the methods was defined within the definition of the function and another outside. This is just to show that this can be done.

Using ES6 we can avoid the function keyword and write instead:

const square = {
  length: 10,
  breadth: 10,

  getArea() {
    return this.length * this.breadth;
  }
};

square.printAll = function() {
  console.log(`length: ${this.length}`);
  console.log(`breadth: ${this.breadth}`);
  console.log(`area: ${this.getArea()}`);
};

square.printAll();

Factory functions

As we cannot modify an already defined function without using dirty tricks (https://stackoverflow.com/questions/2136522/can-you-alter-a-javascript-function-after-declaring-it), we can state that it is not possible to add methods to a factory function after it was defined. All methods have to be defined within it. What we can do is add methods to an object generated by a factory function as we saw earlier.

function Rectangle(length, breadth) {
  return {
    length: length,
    breadth: breadth,
    getArea: function() {
      return this.length * this.breadth;
    }
  };
};

const square = Rectangle(10, 10);

square.printAll = function() {
  console.log(`length: ${this.length}`);
  console.log(`breadth: ${this.breadth}`);
  console.log(`area: ${this.getArea()}`);
};

square.printAll();

Using ES6 syntax

const Rectangle = (length, breadth) => {
  return {
    length: length,
    breadth: breadth,
    getArea() {
      return this.length * this.breadth;
    }
  };
};

const square = Rectangle(10, 10);

square.printAll = function() {
  console.log(`length: ${this.length}`);
  console.log(`breadth: ${this.breadth}`);
  console.log(`area: ${this.getArea()}`);
};

square.printAll();

Functional mixins

Functional mixins are a data structure that provides greater abstraction. They also provide true encapsulation. This is something that even classes cannot provide.

const Rectangle = (newlength, newbreadth) => o => {
//   Here we define all the attributes of a mixin
  let length = newlength;
  let breadth = newbreadth;
  const getArea = () => length * breadth;

// And here we specify wich of the attributes we want to make public
  return Object.assign({}, o, {
    length,
    breadth,
    getArea
  });
};

// We can also define and specify that an attribute is public at the same time
const printAll = () => o =>
  Object.assign({}, o, {
    printAll: () => {
      console.log(`length: ${o.length}`);
      console.log(`breadth: ${o.breadth}`);
      console.log(`area: ${o.getArea()}`);
    }
  });

const square = Rectangle(11, 11)({});
console.log(square.getArea());

// At this point we have two ways of creating the object:
const pipe = (...fns) => x => fns.reduce((y, f) => f(y), x);
// OR...
// import pipe from `lodash/fp/flow`;

const createRectangle = (breadth, length) =>
  pipe(
    Rectangle(breadth, length),
    printAll()
  )({});

// Or more compressed but more difficult to read
const createRectangle2 = (breadth, length) =>
  printAll()(Rectangle(breadth, length)({}));

const square2 = createRectangle(9, 9);
const square3 = createRectangle2(3, 3);
square2.printAll();
square3.printAll();

Classes

There are two main ways of implementing classes in Javascript.

Using a constructor function

Note that in this context, the use of the function keyword cannot be avoided, we cannot use arrow functions because the context is not passed correctly with the keyword this.
Note2: Altough it looks similar, it is very different from a factory function. The use of constructors (or classes) should be avoided as they have many disadvantages(
read more about it here:
https://medium.com/javascript-scene/javascript-factory-functions-vs-constructor-functions-vs-classes-2f22ceddf33e
and here:
https://stackoverflow.com/questions/8698726/constructor-function-vs-factory-functions
). Use factory functions instead.

function Rectangle(length, breadth) {
  this.length = length;
  this.breadth = breadth;

  this.getArea = function() {
    return this.length * this.breadth;
  };
}

Rectangle.prototype.printAll = function() {
  console.log(`The length is: ${this.length}`);
  console.log(`The breadth is: ${this.breadth}`);
  console.log(`The area is: ${this.getArea()}`);
}

const square = new Rectangle(10, 10);
square.printAll();

Using the class syntax introduced in ES6

This is only syntactical sugar of what we did before.

Declaring the method inside the Object:

class Rectangle {
  constructor(length, breadth) {
    this.length = length;
    this.breadth = breadth;
  }

  getArea() {
    return this.length * this.breadth;
  };
}

Rectangle.prototype.printAll = function() {
  console.log(`The length is: ${this.length}`);
  console.log(`The breadth is: ${this.breadth}`);
  console.log(`The area is: ${this.getArea()}`);
};


const square = new Rectangle(10, 10);
square.printAll();

Inheritance in Javascript

Composition is harder with classes.
https://medium.com/javascript-scene/why-composition-is-harder-with-classes-c3e627dcd0aa

The use of classes increases the complexity (which makes the code more prone to errors) and the rigidity (which amplifies the probability of breaking changes through inheritance) of the code.