Methods of Instantiation
Corbin Callais
Posted on June 27, 2020
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
inObject.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.
Posted on June 27, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 30, 2024