JavaScript Instantiation in 5 mins


Effective instantiation patterns in JavaScript and how to use them.

Instantiation refers to the creation of an object. There are multiple patterns that have evolved over time that are used to achieve this. When instantiation patterns are used correctly they can effectively manage inheritance and provide greater code re-usability when creating multiple instances of objects.

Effective patterns can reduce the amount of code we write, the amount of code in memory, and the readability of your code to other devs.

We are going to look at how some of the generally accepted patterns can be created.

Functional Instantiation

This involves creating an empty object, assigning its properties and methods, and then returning the object.

It is the most basic and some might easiest to read form of instantiating.

Every time Plant is instantiated, the code inside that function is duplicated. That means that every single instance of Plant has its own copy of the water and repot method for example.

That’s not such a big deal in our case where we have only a few copies, but it's not optimal when you have hundreds or thousands of instances.

Another potential problem is there is no shared lineage since each instance is distinct, if you wanted to change something, you’d need to do it for every single instance.

// Functionalfunction Plant(name, size, flowerColor) => {
var obj = {};
obj.name = name;
obj.size = size;
obj.flowerColor = flowerColor;

obj.water = function(){
// Code to water plant
console.log("Water the " + this.name)
}

obj.repot = function(){
// Code to repot Plant
console.log("Repotting")
}

return obj;

}
var daffodil = Plant("daffodil", "medium", "yellow")
console.log(daffodil.water()) // Water the daffodil

Functional Shared Instantiation

Like functional invocation in many ways but different in many important ways as well.

With shared functional instantiation, we are able to share methods and properties across all the instantiated objects without unnecessarily storing them in memory. Here I constructed an _extends method in order to achieve that.

Problems arise if I need to change the shared methods and properties. Once instantiated a variable loses its connection to the shared methods. So, any modification there will not be passed along to instances instantiated before the change.

// Functional Shared
function _extend(obj){
var objects = arguments;
Object.assign(obj, ...objects)
return obj
}

function Plant(name, size, flowerColor) {
var obj = {};
obj.name = name;
obj.size = size;
obj.flowerColor = flowerColor;

_extend(obj, plantMethods)

return obj;
}

var plantMethods = {

water: function(){
// Code to water plant
console.log("Water the " + this.name)
},

repot: function(){
// Code to repot Plant
console.log("Repot the plant")
}

}

Prototypical Instantiation

Before we look at this pattern we should first understand the prototypical nature of JS. JS, been a prototypical-based language behaves differently from classical class-based languages such as Java or PHP.

Instead of classes inheriting from classes, objects can inherit from objects. This is achieved through a prototype property that is present on every object. This property contains a constructor as well as other built-in methods that are shared via the prototype chain. Prototypical-based languages can be more expressive and provides a richer set of patterns.

ECMAScript 5 introduced a new method: Object.create(). Calling this method creates a new object. But most importantly what Object.create does is attach methods and properties to the object’s prototype instead of being returned within the object. Every method is available to every object created without duplicating methods in memory.

The method allows us to directly set the __proto__ property in a way that is a single event, which permits the browser to further optimize the object. It also allows the creation of objects without a prototype, using Object.create(null).

function Plant(name, size, flowerColor) {
var obj = Object.create(plantMethods)
obj.name = name;
obj.size = size;
obj.flowerColor = flowerColor;

return obj;
}

var plantMethods = {

water: function(){
// Code to water plant
console.log("Water the " + this.name)
},

repot: function(){
// Code to repot Plant
console.log( "Repot the plant")
}

}
var daffodil = Plant("daffodil", "medium", "yellow")
console.log(daffodil.water()) // Water the daffodil

The slow object initialization can be a performance black hole if using the second argument, because each object-descriptor property has its own separate descriptor object. When dealing with hundreds of thousands of object descriptors in the form of objects, that lag time might become a serious issue.

Pseudo-Classical Instantiation

Pseudo classical instantiation attempts to overcome the amount of typing required to create an object using prototypal instantiation. Like prototypal, Pseudo classical instantiation utilizes the prototype chain.

But, best of all is there is a significant reduction in the amount of code that we would normally contribute because JavaScript does all the following work for us under the hood.

  • Creates a new object.
  • Binds the this keyword to that new object.
  • Sets the new object’s __proto__, or internal prototype property, to be the prototype of the constructing function.
  • At the end of the function, if no other return is specified, returns this (the new object).
function Plant(name, size, flowerColor) {
this.name = name
this.size = size;
this.flowerColor = flowerColor;
}

Plant.prototype.water = function(){
// Code to water plant
console.log("Water the " + this.name)
}

Plant.prototype.repot = function(){
// Code to repot Plant
console.log("Repot the plant")
}
var daffodil = new Plant("daffodil", "medium", "yellow")
console.log(daffodil.water()) // Water the daffodil

ES6 Pseudo-Classical Instantiation

ECMAScript 2015 introduced a new set of keywords implementing classes. The new keywords include class, constructor, static, extends, and super

Essentially, classes allow us to replicate a classical approach to instantiation. However, If we were to look under the hood we would see it still works the same way. It will still contain an internal prototype property and return this as the new object.

class Plant{
constructor(size, flowerColor){
this.size = size;
this.flowerColor = flowerColor;
}

water(){
// Code to water plant
console.log("Water the " + this.name)
}

repot(){
// Code to repot Plant
console.log( "Repot the plant")
}
}
var daffodil = new Plant("daffodil", "medium", "yellow")
console.log(daffodil.water()) // Water the daffodil

Leave a comment


Please note, comments must be approved before they are published