Creating objects with functions, one level deeper.

Creating objects with functions, one level deeper.

JavaScript, Dive Deeper

Basic functionality of function is like a computer. We input some properties, a function does what is defined inside it and gives an output.

In JavaScript a majority of functions are used to create objects. It is because an object can store both Data types and Structural types.

Data types are also called primitives, they include undefined, boolean, number, string, etc. Structural types, also known as non-data types include objects, arrays, functions, etc.

You can find more about Data types and Structural types here.

Lets look at some ways of creating objects using different types of functions.

Factory Function

It is the simplest way of returning the objects.

For example, Let us say, we need an object which stores firstName, lastName of a person and also a method to log the fullName of the person in the console.

It goes like this...

function FacPerson(firstName, lastName){
  return {
    firstName,
    lastName, 
    fullName() {
      console.log(this.firstName + " " + this.lastName);
    }
  }
}

const aPerson = FacPerson("John", "Wick");
aPerson.fullName();

Note that I used this object to call the input i.e., firstName and lastName. We will discuss it shortly.

What is happening in the code? Person function returns an object with firstName, lastName and the method fullName. aPerson is assigned the object generated by Person function which has the values "John" as firstName and "Wick" as lastName. In the last line the fullName method is called. This should log "John Wick" in the console.

Constructor Function

In a constructor function the object is not explicitly returned like in the case of factory function. Instead a this object is implicitly returned when a new instance is created.

It goes like this...

function ConPerson(firstName, lastName){
  this.firstName = firstName;
  this.lastName = lastName;
  this.fullName = function(){
    console.log(this.firstName + " " + this.lastName);
  };
}

const anoPerson = new ConPerson("John", "Wick");
anoPerson.fullName();

Notice we didn't return anything in the above function? It is because this object is implicitly returned.

We will talk about this a little later. Let's talk about new. new creates something called an instance. The instance in this case is an object. So when a new instance is created, the constructor function returns the this object.

Let us see what is happening in the code. When a new instance of Person function is created with values "John" as firstName and "Wick" as lastName, the this object is returned. This returned this object is assigned to aPerson. In the last line the fullName method is called. This should log "John Wick" in the console.

this

Now that we have a preliminary understanding of this lets dive deeper.

The this have a deeper meaning than we discussed above. There are many ways of defining a function,

  1. Function Declaration
  2. Function Expression
  3. Arrow Function

Functions inside an object are called methods. In any object values are stored as key-value pairs. Hence a Function Declaration can't be used for defining object methods.

Let us take a look at how this behaves inside other two types of function.

Let us create an object with methods defined using Function Expression and Arrow Function and log this in the console.

function FactoryFunction() {
    return {
        functionExpression: function() {
            console.log(this);
        },
        arrowFunction: () => {
            console.log(this);
        }
    }
}

const facFun = FactoryFunction();
facFun.functionExpression();
facFun.arrowFunction();

The above code logs the object returned by FactoryFunction in the case of functionExpression whereas in the case of arrowFunction it logs the window object

window object is the object in which details of the entire code is stored.

This is because of how a function expression and arrow function is compiled. A function expression is compiled in its global scope i.e., in the scope of the object inside which it is defined. Where as an arrow function is compiled in lexical scope i.e., in the scope window object.

Now lets see how Constructor Function hold

function ConstructorFunction() {
    this.functionExpression = function () {
        console.log(this);
    }

    this.arrowFunction = () => {
        console.log(this);
    }   
}

const conFun = new ConstructorFunction();
conFun.functionExpression();
conFun.arrowFunction();

The above code logs the object returned by FactoryFunction in both the cases.

This is because of how instances work. When an instance is called, it forms its own this object scope and complies the code. Since it inside it's own object the global and lexical scope are the same and hence gives the same result for both function expression and arrow function.

Use the replit below to test it out:

Conclusion

Arrow Functions have shorter syntax than Function Expression. So when writing a function to return an object with methods which needs to access variables which are in it, It is preferable to write a Constructor Function with Arrow Function methods. Or we need to be careful which type of function is being used for defining methods in the case of Factory Function.