DEV Community

Corbin Callais
Corbin Callais

Posted on

Methods of Instantiation

Instantiation is a term used when describing the declaration of a Constructor function, a function that, well, constructs. More specifically, it is a factory function that is used to generate instances of an object.

These kinds of functions are used primarily in Object-Oriented Programming (OOP) to generate objects that are essential to code without having to define each object individually. This also means they can have their own custom methods, which allow you to add, remove, locate, and otherwise do as you please with said objects, either individually or all at once.

Just like how there are multiple ways you can declare a function, you can instantiate constructors in multiple ways. There are 5 different ways: Functional, Functional-Shared, Prototypal, Pseudoclassical, and ES6 Pseudoclassical.

Functional

Functional is, as its name implies, instantiation that is no different than declaring any other function. All of the properties and methods are right there in the object you generate with this kind of constructor.

For sake of examples, let's say you want to make a constructor for mass-producing Car objects.

// First, you would define the function. It's customary to capitalize the first letter, unlike normal camelCase.
function Car(model, make, owner) {

    // Then, define an object. It's also customary to name the object we'll eventually return the same as the constructor.
    const car = {};

    // You can define the values inside or outside (with dot notation), it doesn't matter.
    car.model = model;
    car.make  = make;
    car.owner = owner;

    // We can also create custom methods the same way, but assigning functions.
    car.drive = function() {console.log('Vroom!');}

    // Don't forget to return the object when you're done.
    return car;
}

// And now, if we want to create an object, we simply call it.
const mikesChevySilverado = Car('Silverado', 'Chevrolet', 'Mike');

Upsides:

  • All of the properties are inside of the object for reference
  • By far the simplest/easiest method.
    • We don't have to deal with mumbo jumbo that the later methods do. #### Downsides
  • The methods are inside the object alongside the properties. This can hurt situationally, such as finding how many keys there are in a single object.
  • Being the simplest method, it's also the least optimized to handle generation. Unless there's a specific reason to, or you're learning, it's almost always preferred to use a more optimized method.

Functional-Shared

Functional-Shared is functionally identical to Functional at the end of the day. The key difference between the two is that instead of defining methods inside of the object, you define them in a separate object and extend the constructed function.

Example:

// Same setup as before...
function Car(model, make, owner) {
    const car = {};
    car.model = model;
    car.make  = make;
    car.owner = owner;

    // ...Up to this point. Here we do an extend function, typically by either native or Underscore.JS.
    // The object we're extending car from is at the bottom.
    _.extend(car, carMethods);

    return car;
}

// Here is where we define the methods. This is the object we extend the constructor object with.
const carMethods = {
    drive: function() {
        console.log("Vroom!");
    },
    start: function() {
        /* [...] */
    },
    stop: function() {
        /* [...] */
    }
};

// And now the same as before to make one.
const joeysHondaAccord = Car('Accord', 'Honda', 'Joey');

Upsides

  • All of the methods are in one place, making it easier to change what they do for all instances.
  • Slightly splits up and cuts down on the code involved in making the constructor object, leaving a tidier workspace.
  • Minorly cuts down on memory used, as instead of every object having their own instance of a method, they all point to the original instead.

Downsides

  • As all of the methods are referencing to the original object, changing the method will effect all instances, not just one. This is not ideal for having special objects that have some sort of individuality (like more specific classes of cars).
  • The methods are still in the object, which poses the same related problems as with Functional.

Prototypal

Prototypal is the first of this list that doesn't have the methods in the object, but instead in its prototype, hence its name. The prototype is the hidden property all datatypes in JavaScript has that provides properties gained through inheritance. Inheritance is simply a way to give properties to instances of objects.

// From now on, we will use "const <name> = function() {}". It's a more "proper" way to declare functions as of ES6.

const Car = function(model, make, owner) {
    // This is the major change in Prototypal. We use Object.create(), a native Object object method that allows all arguments to go into the created object's prototype. How convenient.
    const car = Object.create(carMethods);
    car.model = model;
    car.make  = make;
    car.owner = owner;

    return car;
};

const carMethods = {
    drive: function() {
        console.log("Vroom!");
    },
    start: function() {
        /* [...] */
    },
    stop: function() {
        /* [...] */
    }
};

console.log(Car("Corolla", "Toyota", "James"));
// Logs {model: "Corolla", make: "Toyota", owner: "James"}. Note how if we were to use Functional or Functional-Shared, we'd also see the methods.

Upsides

  • The methods are out of the main object, preventing any mistakes stemming from such a problem.
  • Uses fancier code to cut down on what you need to write for the same purpose.

Downsides

  • The methods are still shared, only they have changed location. Changing one for any reason will affect them all.
  • Typically will require a deeper understanding of the native constructors, like Object in Object.create(), in order to use effectively.

Pseudoclassical

Pseudoclassical is considered the "de-facto" method for instantiation, for good reason. It's considered the most optimized more most cases, only being beaten out by ES6's syntactic sugar (it's still functionally identical, though).

Instead of defining the methods on the constructed object, they define them on the constuctor function's prototype, allowing all of the constructed objects to have them, with help with the new keyword.

Additionally, with the new keyword, we don't even need to define the object or return it. We just use this, applying all the applicable properties to it.

// Mostly the same setup, except...
const Car = function(model, make, owner) {
    // We don't even define an object, or return anything. It just knows that "this" is what we're generating.
    this.model = model;
    this.make  = make;
    this.owner = owner;
};

// And then we define the methods on the constructor's prototype. Don't worry too hard on how it works, the "new" keyword just knows.
Car.prototype.drive = function () {
    console.log("Vroom!");
};
Car.prototype.start = function () {
    /* [...] */
};
Car.prototype.stop  = function () {
    /* [...] */
};

// And now to define it.
const samsFordFocus = new Car("Focus", "Ford", "Sam");

Upsides

  • Much more visually friendly than the other two. I mean, the base function is only three lines.
  • Makes use of the word this, something that is typed faster than most all other names for objects, increasing work efficiency.

Downsides

  • Much more complex, would require a major amount of studying to obtain a deep understanding of.
  • Introduces the new keyword before calling the function. It trips up a lot more new people than one may think.

ES6 Pseudoclassical

By far my personal favorite, ES6 Pseudoclassical is the most user friendly, albeit looking entirely different than the other instantiation types.

It makes use of the new class keyword introduced in ES6, which does all the hard work for you. Inside of the class block, there is a constuctor() function you must define. Below that, however, still within the class block, you define your methods by simply putting their name, their parameters, and their code block.

// Entirely different setup. First, the class...
class Car {
    // Then the constructor. This allows the object generated to have properties.
    constructor(model, make, owner) {
        this.model = model;
        this.make  = make;
        this.owner = owner;
    }
    // Everything below it is put into its prototype. Like methods.
    drive() {
        console.log("Vroom!");
    }
    start() {
        /* [...] */
    }
    stop() {
        /* [...] */
    }
}

// To call it, it's the same as Pseudoclassical. Don't forget the "new" keyword.
const aaronsKiaSorento = new Car("Sorento", "Kia", "Aaron");

Upsides

  • Much simpler, more user friendly, and stands out more than any boring old function.
  • Does most of the heavy lifting for you.

Downsides

  • Being ES6, it requires specifications to allow it to run. Typically not a problem in this day and age, but older browsers don't understand ES6.
  • Also because ES6, it requires you to learn an entire new set of syntax to use and understand this. Now a problem for newer learners, but people coming from ES5 and earlier would have some issues getting used to it.

Conclusion

Each type of instantiation all does one core thing, that being generating instances of objects that have certain properties and methods to use in your code.

Functional and Functional-Shared are the most familiar to novice coders, since they're just functions and objects with minor differences between the two.

Prototypal Makes use of the prototype chain, making it slightly more complex just to not include the methods in the main object.

Pseudoclassical is the most optimized for the majority of cases, and ES6 Pseudoclassical is just the ES6 version of that, functionally identical but much easier on the eyes.

Top comments (0)