Using Constructors in JavaScript
A lot of people are using constructors but not everybody know how exactly they are working. The main reason of current article to show the nature of JavaScript constructors.
Firstly let’s take a look on most common pattern of constructor in JavaScript. So here is an example of simple constructor:
// first version
function Person(name) {
// set default value for name
this.name = name || 'Default';
}// second version
var Person = function(name) {
// set default value for name
this.name = name || 'Default';
}
Nothing special here. From the first glance it looks like a normal function with first capital letter (by the way it’s a convention for constructor functions), but it is not exactly the true. There are few things that you should know about default constructor pattern. Let’s take a look on the following lines of code:
// lets invoke that function without new
// keyword and save result in person variable
var person = Person();// TypeError: Cannot read property 'name' of undefined
// console.log(person.name); // nothing new was created but...
console.log(window.name); // Default
What? It happen because Person constructor was invoked as normal function and this inside of Person function in current case was window, global JavasScript object, so we define new property name with value ‘Default’ in window object. I guess that side effect was exactly what we didn’t want to do. In order to avoid that case there is two ways:
- We should invoke Person function with new keyword. (When you add new keyword before constructor function it means that in the end of function in current case it is Person() will be implicitly created and returned new instance.
- We should use Self Invoking Constructor pattern
First version with new keyword:
var person = new Person('John');
console.log(person.name); // John
console.log(window.name); // undefined
Second version with Self Invoked Constructor Pattern:
// first version
function Person(name) {
if(!(this instanceof Person))
return new Person(name);
// set default value for name
this.name = name || 'Default';
}// second version
var Person = function() {
if(!(this instanceof Person))
return new Person(name);
// set default value for name
this.name = name || 'Default';
}
Here we are checking if this is instance of Person and if it’s not a window object, if condition is true we proceed as normal constructor, in case if this is a window object or some other (for example Node.js has other global object) we recursively invoke constructor(function) with new keyword. So here we are solving problem with adding new properties to global object. Using this pattern we are able to implement some patterns for example builder.
Here is a quick example of Builder and Chain Pattern using Self Invoked Constructor:
var log = console.log;function Person(name, age, city) {
if(!(this instanceof Person))
return new PersonBuilder();
this.name = name;
this.age = age;
this.city = city;
}function PersonBuilder() {
let person = Object.create(Person.prototype);
this.get = function() {
return person;
}
}PersonBuilder.prototype.setName = function(name) {
var person = this.get();
person.name = name;
return this;
}PersonBuilder.prototype.setAge = function(age) {
var person = this.get();
person.age = age;
return this;
}PersonBuilder.prototype.setCity = function(city) {
var person = this.get();
person.city = city;
return this;
}var person = new Person('Oleh', 29, 'Lviv');
log(person); // { age: 29, city: "Lviv", name: "Oleh" }
log(person.constructor.name); // Personvar person2 = Person()
.setAge(27).setName('John').setCity('NY').get();
log(person2); // { age: 27, city: "NY", name: "John" }
log(person2.constructor.name); // Person
So far so good. I don’t want to deep inside of explanation how current pattern works because it’s far from current scope of article. Builder also can be organised in many other ways. The only thing that you should know it that Self Invoking Constructor pattern is pretty helpful. For example we can improve current implementation and create validators inside of builder and even add chain function returnIfValid() instead of get() function. I guess there is many ways to improve current pattern combination.
If constructor don’t take properties, or you don’t wanna to initialize current properties from outside, constructor can be invoked without brackets. For example JavaScript Date object can be created without brackets, it just has many overloads including no parameter constructor.
Here is an example:
function Person() {
this.name = 'John';
}var person = new Person;
log(person); // { name: "John"}
log(person.constructor.name); // Person
log(person.constructor === Person); // true
log(new Date); // ...
ES6 provides new syntax for creating objects. Now we have a classes, constructors, super keyword and so on.
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
It’s always your choice what syntax to use. As for me first version is more flexible and understandable.
Now we have new question how to add private members to our constructor? And the answer is simple:
- just don’t add it to this
- provide privileged methods
yup and that’s all, here is an example:
function Person() {
var name;
this.getName = function() {
return name;
}
this.setName = function(val) {
name = val;
}
}
var person = new Person();
person.setName(‘Oleh’);
log(person.getName()); // Oleh
But that approach is not always good because with each instance you create getName() and setName() function. So once again it’s your choice if it’s really needed for you.
And last topic that should be covered is a static members, in my opinion it’s related theme.
function Person (name) {
this.name = name;
}// Static variable shared by all instances
Person.staticProperty = “baz”;
Person.staticMethod = function() {
return ‘hello from person static function’;
}// DEMO:
var person = new Person(‘Oleh’);
log(person.staticProperty); // undefined
log(person.staticMethod); // undefined
log(Person.staticProperty); // baz
log(Person.staticMethod()); // hello from person static function
and private static members example:
var Person = (function () {
// Private static property.
var numOfPersons = 0;
// Private static method.
function countLogger(name) {
log(`Was created new Person with name: ${name} and id: ${numOfPersons}`);
}
// Return the constructor.
return function (name) {
numOfPersons++;
this.name = name;
countLogger(name);
}
})();// DEMO:
var person = new Person(‘Oleh’); // “Was created new Person with name: Oleh and id: 1”
var person2 = new Person(‘John’); // “Was created new Person with name: John and id: 2”
In previous examples you can see that static variables was created only once, and it is exactly what we needed.
Conclusion
Thank you guys for reading. I hope you enjoyed it and learned some new stuff related to JavaScript. Please subscribe and press ‘Clap’ button if you like this.