- Constructor, operator «new»
- Constructor function
- Constructor mode test: new.target
- Return from constructors
- Methods in constructor
- Summary
- JavaScript Constructor Function
- Introduction to JavaScript constructor functions
- Adding methods to JavaScript constructor functions
- Returning from constructor functions
- Calling a constructor function without the new keyword
- Summary
Constructor, operator «new»
The regular <. >syntax allows us to create one object. But often we need to create many similar objects, like multiple users or menu items and so on.
That can be done using constructor functions and the «new» operator.
Constructor function
Constructor functions technically are regular functions. There are two conventions though:
- They are named with capital letter first.
- They should be executed only with «new» operator.
function User(name) < this.name = name; this.isAdmin = false; >let user = new User("Jack"); alert(user.name); // Jack alert(user.isAdmin); // false
When a function is executed with new , it does the following steps:
- A new empty object is created and assigned to this .
- The function body executes. Usually it modifies this , adds new properties to it.
- The value of this is returned.
In other words, new User(. ) does something like:
function User(name) < // this = <>; (implicitly) // add properties to this this.name = name; this.isAdmin = false; // return this; (implicitly) >
So let user = new User(«Jack») gives the same result as:
Now if we want to create other users, we can call new User(«Ann») , new User(«Alice») and so on. Much shorter than using literals every time, and also easy to read.
That’s the main purpose of constructors – to implement reusable object creation code.
Let’s note once again – technically, any function (except arrow functions, as they don’t have this ) can be used as a constructor. It can be run with new , and it will execute the algorithm above. The “capital letter first” is a common agreement, to make it clear that a function is to be run with new .
If we have many lines of code all about creation of a single complex object, we can wrap them in an immediately called constructor function, like this:
// create a function and immediately call it with new let user = new function() < this.name = "John"; this.isAdmin = false; // . other code for user creation // maybe complex logic and statements // local variables etc >;
This constructor can’t be called again, because it is not saved anywhere, just created and called. So this trick aims to encapsulate the code that constructs the single object, without future reuse.
Constructor mode test: new.target
The syntax from this section is rarely used, skip it unless you want to know everything.
Inside a function, we can check whether it was called with new or without it, using a special new.target property.
It is undefined for regular calls and equals the function if called with new :
function User() < alert(new.target); >// without "new": User(); // undefined // with "new": new User(); // function User
That can be used inside the function to know whether it was called with new , “in constructor mode”, or without it, “in regular mode”.
We can also make both new and regular calls to do the same, like this:
function User(name) < if (!new.target) < // if you run me without new return new User(name); // . I will add new for you >this.name = name; > let john = User("John"); // redirects call to new User alert(john.name); // John
This approach is sometimes used in libraries to make the syntax more flexible. So that people may call the function with or without new , and it still works.
Probably not a good thing to use everywhere though, because omitting new makes it a bit less obvious what’s going on. With new we all know that the new object is being created.
Return from constructors
Usually, constructors do not have a return statement. Their task is to write all necessary stuff into this , and it automatically becomes the result.
But if there is a return statement, then the rule is simple:
- If return is called with an object, then the object is returned instead of this .
- If return is called with a primitive, it’s ignored.
In other words, return with an object returns that object, in all other cases this is returned.
For instance, here return overrides this by returning an object:
function BigUser() < this.name = "John"; return < name: "Godzilla" >; // alert( new BigUser().name ); // Godzilla, got that object
And here’s an example with an empty return (or we could place a primitive after it, doesn’t matter):
function SmallUser() < this.name = "John"; return; // alert( new SmallUser().name ); // John
Usually constructors don’t have a return statement. Here we mention the special behavior with returning objects mainly for the sake of completeness.
By the way, we can omit parentheses after new :
Omitting parentheses here is not considered a “good style”, but the syntax is permitted by specification.
Methods in constructor
Using constructor functions to create objects gives a great deal of flexibility. The constructor function may have parameters that define how to construct the object, and what to put in it.
Of course, we can add to this not only properties, but methods as well.
For instance, new User(name) below creates an object with the given name and the method sayHi :
function User(name) < this.name = name; this.sayHi = function() < alert( "My name is: " + this.name ); >; > let john = new User("John"); john.sayHi(); // My name is: John /* john = < name: "John", sayHi: function() < . >> */
To create complex objects, there’s a more advanced syntax, classes, that we’ll cover later.
Summary
- Constructor functions or, briefly, constructors, are regular functions, but there’s a common agreement to name them with capital letter first.
- Constructor functions should only be called using new . Such a call implies a creation of empty this at the start and returning the populated one at the end.
We can use constructor functions to make multiple similar objects.
JavaScript provides constructor functions for many built-in language objects: like Date for dates, Set for sets and others that we plan to study.
In this chapter we only cover the basics about objects and constructors. They are essential for learning more about data types and functions in the next chapters.
After we learn that, we return to objects and cover them in-depth in the chapters Prototypes, inheritance and Classes.
JavaScript Constructor Function
Summary: in this tutorial, you’ll learn about the JavaScript constructor function and how to use the new keyword to create an object.
Introduction to JavaScript constructor functions
In the JavaScript objects tutorial, you learned how to use the object literal syntax to create a new object.
For example, the following creates a new person object with two properties firstName and lastName :
let person = < firstName: 'John', lastName: 'Doe' >;
Code language: JavaScript (javascript)
In practice, you often need to create many similar objects like the person object.
To do that, you can use a constructor function to define a custom type and the new operator to create multiple objects from this type.
Technically speaking, a constructor function is a regular function with the following convention:
- The name of a constructor function starts with a capital letter like Person , Document , etc.
- A constructor function should be called only with the new operator.
Note that ES6 introduces the class keyword that allows you to define a custom type. And classes are just syntactic sugar over the constructor functions with some enhancements.
The following example defines a constructor function called Person :
function Person(firstName, lastName) < this.firstName = firstName; this.lastName = lastName; >
Code language: JavaScript (javascript)
In this example, the Person is the same as a regular function except that its name starts with the capital letter P .
To create a new instance of the Person , you use the new operator:
let person = new Person('John','Doe');
Code language: JavaScript (javascript)
Basically, the new operator does the following:
- Create a new empty object and assign it to the this variable.
- Assign the arguments 'John' and 'Doe' to the firstName and lastName properties of the object.
- Return the this value.
It’s functionally equivalent to the following:
function Person(firstName, lastName) < // this = <>; // add properties to this this.firstName = firstName; this.lastName = lastName; // return this; >
Code language: JavaScript (javascript)
Therefore, the following statement:
let person = new Person('John','Doe');
Code language: JavaScript (javascript)
… returns the same result as the following statement:
let person = < firstName: 'John', lastName: 'Doe' >;
Code language: JavaScript (javascript)
However, the constructor function Person allows you to create multiple similar objects. For example:
let person1 = new Person('Jane','Doe') let person2 = new Person('James','Smith')
Code language: JavaScript (javascript)
Adding methods to JavaScript constructor functions
An object may have methods that manipulate its data. To add a method to an object created via the constructor function, you can use the this keyword. For example:
function Person(firstName, lastName) < this.firstName = firstName; this.lastName = lastName; this.getFullName = function ( ) < return this.firstName + " " + this.lastName; >; >
Code language: JavaScript (javascript)
Now, you can create a new Person object and invoke the getFullName() method:
let person = new Person("John", "Doe"); console.log(person.getFullName());
Code language: JavaScript (javascript)
The problem with the constructor function is that when you create multiple instances of the Person , the this.getFullName() is duplicated in every instance, which is not memory efficient.
To resolve this, you can use the prototype so that all instances of a custom type can share the same methods.
Returning from constructor functions
Typically, a constructor function implicitly returns this that set to the newly created object. But if it has a return statement, then here’s are the rules:
- If return is called with an object, the constructor function returns that object instead of this .
- If return is called with a value other than an object, it is ignored.
Calling a constructor function without the new keyword
Technically, you can call a constructor function like a regular function without using the new keyword like this:
let person = Person('John','Doe');
Code language: JavaScript (javascript)
In this case, the Person just executes like a regular function. Therefore, the this inside the Person function doesn’t bind to the person variable but the global object.
If you attempt to access the firstName or lastName property, you’ll get an error:
console.log(person.firstName);
Code language: CSS (css)
TypeError: Cannot read property 'firstName' of undefined
Code language: JavaScript (javascript)
Similarly, you cannot access the getFullName() method since it’s bound to the global object.
person.getFullName();
Code language: CSS (css)
TypeError: Cannot read property 'getFullName' of undefined
Code language: JavaScript (javascript)
To prevent a constructor function to be invoked without the new keyword, ES6 introduced the new.target property.
If a constructor function is called with the new keyword, the new.target returns a reference of the function. Otherwise, it returns undefined .
The following adds a statement inside the Person function to show the new.target to the console:
function Person(firstName, lastName) < console.log(new.target); this.firstName = firstName; this.lastName = lastName; this.getFullName = function ( ) < return this.firstName + " " + this.lastName; >; >
Code language: JavaScript (javascript)
The following returns undefined because the Person constructor function is called like a regular function:
let person = Person("John", "Doe");
Code language: JavaScript (javascript)
undefined
Code language: JavaScript (javascript)
However, the following returns a reference to the Person function because it’s called with the new keyword:
let person = new Person("John", "Doe");
Code language: JavaScript (javascript)
[Function: Person]
Code language: JSON / JSON with Comments (json)
By using the new.target , you can force the callers of the constructor function to use the new keyword. Otherwise, you can throw an error like this:
function Person(firstName, lastName) < if (!new.target) < throw Error("Cannot be called without the new keyword"); > this.firstName = firstName; this.lastName = lastName; >
Code language: JavaScript (javascript)
Alternatively, you can make the syntax more flexible by creating a new Person object if the users of the constructor function don’t use the new keyword:
function Person(firstName, lastName) < if (!new.target) < return new Person(firstName, lastName); > this.firstName = firstName; this.lastName = lastName; > let person = Person("John", "Doe"); console.log(person.firstName);
Code language: JavaScript (javascript)
This pattern is often used in JavaScript libraries and frameworks to make the syntax more flexible.