I recently received a question from someone going through my Mastering JavaScript “this” email course, regarding day 3 of the course, where I talk briefly about constructor functions and the “new” keyword.
In this course, I say:
The value of “this” inside of the constructor function is a new instance of that object type, where the “type” is defined by the constructor function, itself.
The question from the reader centered around type definitions and functions:
How does a constructor function define a type? Isn’t it just a function? What qualifies a function as a constructor function – other than the ‘new’ invocation pattern?
It’s a question that confuses a lot of developers – myself included. But, honestly, that’s about it. It is possible for any valid JavaScript function to be a constructor function, just by adding the “new” keyword in front of it.
But that doesn’t mean every function out there is designed to work this way.
Constructing An Object From A Function
Every function you define in JavaScript has the potential to be a constructor function. You can see evidence of this by looking at the “.prototype” attribute of any function you define (in Chrome DevTools, for example.
The .prototype attribute of the function is used as the prototype of the object instance, when you call the function with the “new” keyword.
The object is also said to be an instance of that function’s name… the “type” that was defined with the function.
The “s” object in these examples is clearly an instance of “something” with a prototype defined by that function.
But, how does that happen?
The Magic Of “new”
Behind the scenes, when you call a function with the “new” keyword, the JavaScript runtime is basically doing this:
- Create a new object instance
- Assign it’s prototype to the function’s .prototype
- Apply the function with the object instance set as “this”
Granted, this is a greatly over-simplified representation of what happens, but it is the core of the process.
Because every function you define has a .prototype, and because of the way the “new” keyword behaves, it is possible to use just about any function as a constructor function to create new instances of that “type”.
But just because you can, doesn’t mean you should.
Not All Functions Are Created Equal
Most functions are just functions that do some work and maybe return some value.
You could use the “new” keyword in front of any random function, if you wanted to, but the results would not be predictable if it was not intended to be a constructor function.
For example, look at the following code and ask yourself what it would return as a plain function call vs a construction function call.
If you call this as a plain function, it will return the string as you would expect.
But what happens if you call it as a constructor?
In this case, the function no longer works as expected – the string that you wanted will not be returned.
Yet, in another example, the return value of the normal function will still return as expected:
In this case, both the standard function call and the constructor function call return the same value.
When you have a constructor function, there is an implicit return value – the new object instance. You are allowed to override this return value with your own object instance, though – as shown in the second example.
What you are not allowed to do, is override the object return value with a primitive value like a string. The Javascript runtime will ignore the primitive return value and send back the object instance, instead.
Don’t worry, though. It gets worse!
Object Methods Are Terrible Constructors
In object-oriented JavaScript, an object typically has methods. These methods often make use of the “this” keyword.
So, what happens when you have an object with a method, like this one below, and you apply the “new” keyword to it?
The result is not a pretty site:
The use of this function as a constructor has clearly broken things.
The value of “this”, when the function is used as a constructor, is not the value of “this” that the function expected. It was designed to use the object itself as “this”, but that expectation was not met. The code tried to run anyways, and very bad things happened as a result.
Because of this – and other potential hazards when dealing with functions as constructor functions – there are some common tactics for letting other developers know which functions should be used as constructor functions.
Idiomatic Constructor Functions
In general, there is a common practice for naming functions with a capitalized name, to identify them as a constructor function. They also tend to be named as a noun.
For example:
being a capitalized function name, and being a noun, there’s a high likelihood that this is supposed to be a constructor function. Whereas, this function:
is not likely to be a constructor function.
The naming convention follows a standard for a function that just returns a value. It’s not capitalized, and it has the word “get” in it – a word that typically means the function will return some value.
There isn’t anything special about capitalized names for functions, though, other than a generally agreed upon standard by which most JavaScript developers work. This is, by definition, idiomatic JavaScript.
Unfortunately this mean the the standard practices of JavaScript function names are often the only thing that separate you from a new object vs a runtime disaster. At least, until ES6 classes came around.
ES6 Classes And Type Definitions
With ES6 (officially ES2015), JavaScript tried to clarify type definitions by introducing “class” syntax. If you’ve ever worked in C#, Java or other C-syntax languages with classes, this syntax should look fairly familiar.
In this example, a simple “Bar” class is defined and a “doStuff” method is added. An object instance is created and the doStuff method is executed as expected.
What you might not realize, with this syntax, is that it is mostly just syntax sugar on top of the existing constructor functions of JavaScript.
Yes, there are some real differences in class syntax vs constructor function syntax, but once a class is defined, it is actually a constructor function (as shown by Node v4+):
As you can see in this output, the class Bar does produce a constructor function. However, it is not a “normal” function in that it cannot be called without “new”
This is one of many differences with ES6 Classes.
The behavior of ES6 classes may be somewhat different, but in the end, it is still a constructor function with a .prototype that defines the JavaScript type at runtime.
Functions And Type Definitions
The ability to use nearly any function as a constructor by using the “new” keyword means it is possible to get yourself into some sticky situations if you’re not careful. Just because you can use any function as a constructor, doesn’t mean you should. Idioms and good document should exist to help us identify which functions should be used this way.
The ES6 syntax for class definitions attempts to move JavaScript away from this danger zone, but still builds upon the original concepts of constructor functions. And creating more possibilities, still, it is entirely possible to build object instances with prototypes, not using constructor functions. This is prototypal inheritance, after all – even with ES6 classes.
Want To Know More About Types And Prototypes?
If you’re interested in knowing more about JavaScript’s type and prototype, check out the WatchMeCode screencasts covering both JavaScript fundamentals.
With the JavaScript fundamentals series, you’ll learn everything you need to know about objects, functions, prototypes, and more. From simple object definitions to object composition, from prototypal inheritance to constructor functions, the JavaScript Objects And Prototypes episode will cover everything you need. Then, with additional episodes covering variable scope, the dreaded “this” keyword and so much more, you’ll be on your way to mastering JavaScript, fast!
Learn more JavaScript in minutes than you have in years with WatchMeCode screencasts.