Constructors & new
Create many similar JavaScript objects from one function, understand what new does, guard constructors with new.target, and predict constructor return values.
- 01Make many similar objectsWrite a constructor function, with a capital name, that stores values on
this. - 02Follow the steps of
newExplain the four steps: make an empty object, link it to the prototype, run the function withthisset to it, and give it back. - 03Handle the unusual casesUse
new.targetto catch a missingnew, predict what a constructor'sreturndoes, and avoid wrapper objects.
One recipe, many objects
An object literal is the curly-brace way to write one object, like { name: "Ada" }. It is great when you need just one object.
But apps often need many objects with the same shape. Think of many users, many books, or many items in a shopping cart. Each one has the same property names, but different values.
A constructor function is a function that makes these objects for you. You write it once. Then you call it again for each new object. Constructor functions are an older JavaScript pattern, and you still see them in a lot of code.
You call a constructor with the word new, like new User("Ada"). In short, this is what happens:
- JavaScript makes a new, empty object.
- Inside the constructor,
thismeans that new object. - The constructor writes properties onto
this. newgives the finished object back to you.
This lesson uses two things you already know. The first is functions, from Declaring & calling functions. The second is methods, where this is the object before the dot.
A constructor is like a cookie cutter. Calling it with new is like pressing the cutter into fresh dough. Every press makes a new cookie with the same shape. But each cookie can have its own sprinkles.
- In real life: The cookie cutter
- In JavaScript: The constructor function, such as
User - In real life: Pressing the cutter into dough
- In JavaScript: Calling it with
new User(...) - In real life: Each cookie
- In JavaScript: Each separate object that
newgives back - In real life: Sprinkles on one cookie
- In JavaScript: Values stored on one object, like
nameandrole
Modern JavaScript usually writes this pattern with classes. A class is a newer way to write a constructor. It works the same way inside: new makes an object and links it to shared methods. Later lessons cover this in detail: The prototype chain, F.prototype & constructors, and Class basics. This lesson gives you the base for them.
Constructor functions
FOUNDATIONA constructor function is a normal function. The only difference is how you call it: with new.
By habit, its name starts with a capital letter: User, Book, Task. JavaScript does not need the capital. It is a sign for people who read the code: "call this with new."
function User(name) {
this.name = name;
this.role = "learner";
}
const ada = new User("Ada");
const grace = new User("Grace");
console.log(ada.name, grace.name);
console.log(ada === grace);- Line 1 starts the
Userconstructor. It takes one input,name. - Lines 2 and 3 write two properties onto
this:nameandrole. - On line 6,
newmakes a new, empty object. Then it runsUserwiththisset to that object. So the object inadagets its ownnameandrole. - Line 7 calls the same constructor again. This makes a second, different object, for Grace.
- Line 8 prints
Ada Grace. Line 9 printsfalse, because they are two different objects.
User function, called twice with new, makes two separate objects. Each object keeps its own name, so ada === grace is false.function Task(title) {
this.title = title || "Untitled";
}
const task = new Task("");
console.log(task.title);A constructor can also check an input before it stores it. On line 2, title || "Untitled" means: use title, but if it is empty, use "Untitled" instead.
Line 5 passes an empty title, so the task's title becomes "Untitled". That value is stored on this one task only.
Three things to remember from this section:
ada.nameis"Ada"andgrace.nameis"Grace". Each object keeps its own values.ada === graceisfalse. Eachnewcall makes a separate object.- You can't use an arrow function with
new. Arrow functions can't be constructors, and they don't have their ownthis.
If you call User("Ada") without new, no new object is made. What happens next depends on the mode the code runs in:
- In strict mode,
thisisundefined. Strict mode is the safer mode that modules and classes always use. Sothis.name = nametries to write toundefinedand throws aTypeError. - In older, non-strict scripts,
thisis the global object, which every script on the page shares. So the same line quietly creates a globalname. That is worse, because nothing warns you.
What new does
STEP THROUGHHere is what new User("Ada") does, in four steps. These steps are for a constructor function like User. (A class that extends another class follows slightly different steps. A later lesson covers that.)
- Make a new, empty object.
- Link that object to
User.prototype, an object where shared methods live. You'll see what the link does below. - Run
Userwiththisset to the new object. - Give back the new object. The one exception: if
Userreturns its own object,newgives back that object instead.
new: make an empty object, let User fill it through this, then give it back. Step 2, the link to User.prototype, is in the picture under "Shared methods".Think of new as a bakery's routine. First the baker grabs a fresh tray and stamps the shop logo on it. Then your recipe decorates the pastry. Last, the baker hands the pastry to the customer. Your constructor only does the decorating. The bakery does the other three steps for you.
- In real life: Grab a fresh tray
- In JavaScript: Make a new, empty object
- In real life: Stamp the shop logo on it
- In JavaScript: Link it to
User.prototype - In real life: Decorate the pastry
- In JavaScript: Run
Userwiththisset to the object - In real life: Hand it to the customer
- In JavaScript: Give back the finished object
Step 2, the link, is easy to miss. This code shows what it does.
function User(name) {
this.name = name;
}
User.prototype.describe = function () {
return this.name + " is here";
};
const ada = new User("Ada");
console.log(ada.describe());
console.log(Object.hasOwn(ada, "describe"));- Line 4 puts a
describemethod onUser.prototype. It does not put it onada. - Line 10 uses
Object.hasOwn(ada, "describe"). It asks: isdescribestored onadaitself? The answer isfalse. - Yet line 9,
ada.describe(), still works and printsAda is here.
Why does line 9 work? When JavaScript can't find a property on the object itself, it follows the link to User.prototype and looks there.
The replay below copies the four steps by hand, in a small function called createWith. It is only a model to help you see the steps. In real code, you just use new. It uses a few tools you may not have seen yet:
...argscollects the extra inputs into an array calledargs.Object.create(proto)makes a new, empty object linked toproto. That is steps 1 and 2.Constructor.apply(obj, args)runs the function withthisset toobj, and passes in the values fromargs. That is step 3.- Lines 4 to 6 are step 4. If the constructor returned an object or a function, give that back. If not, give back
obj. Object.getPrototypeOf(x)tells you which objectxis linked to.x instanceof Userchecks whetherxis linked toUser.prototype.
The last three lines compare createWith(User, "Ada") with real new User("Ada"). Both objects have the same name and the same link, so all three lines print true.
This model copies the four steps of new: make an object, link it, run the constructor, give it back. Real code just uses new.
script
function createWith(Constructor, ...args) { const result = Constructor.apply(obj, args); return result !== null && (typeof result === "object" || typeof result === "function") ? result : obj;} function User(name) { this.name = name;} const real = new User("Ada");const model = createWith(User, "Ada");console.log(real.name === model.name);console.log(Object.getPrototypeOf(real) === Object.getPrototypeOf(model));console.log(model instanceof User);new.target
INTERACTIVEInside a function written with the function keyword, you can read new.target. It tells you how the function was called:
- Called with
new,new.targetis the function itself. - Called without
new,new.targetisundefined.
function showCall() {
console.log(new.target ? "constructor call" : "plain call");
}
showCall();
new showCall();- Line 2 uses
condition ? a : b. It means: if the condition is true, usea. If not, useb. - Line 5 calls
showCall()withoutnew, so it prints"plain call". - Line 6 calls it with
new, so it prints"constructor call".
The code inside the function is the same both times. Only new.target changes.
A constructor can use this to catch a missing new. It has two choices:
- Fix the call:
if (!new.target) return new SafeUser(name). The!means "not", so this line runs whennewwas left out. It calls the function again, this time withnew. - Stop with an error: throw a
TypeError, so the mistake shows up right away. Exercise 4 practices this.
The replay below uses the first choice. Pick the call on line 8, then step through it.
Switch the call to see new.target change. It is undefined when you leave out new, and the function itself when you use new.
script
function SafeUser(name) { if (!new.target) { return new SafeUser(name); } this.name = name;} console.log(ada.name);Returning from constructors
EDGE CASESMost constructors don't have a return line. They write to this, and new gives back that object.
But a constructor can return something. Then one unusual rule applies:
- If it returns an object, that object takes the place of the object
newmade. - If it returns a primitive,
newignores it. A primitive is a simple value that is not an object: a number, a string, a boolean,null,undefined, a symbol, or a bigint.
function ReturnsObject() {
this.name = "built";
return { name: "replacement" };
}
function ReturnsPrimitive() {
this.name = "built";
return "ignored";
}
console.log(new ReturnsObject().name);
console.log(new ReturnsPrimitive().name);- Line 3 returns an object. So line 11 prints
replacement, notbuilt. - Line 8 returns a string.
newignores it, so line 12 printsbuilt.
new made. Right: it returns a string, a primitive, so new ignores it and you get the object it made.function Ticket(code) {
this.code = code;
return 0;
}
console.log(new Ticket("A1").code);Line 3 returns 0, a number. new ignores it and keeps the ticket it made, so line 6 prints A1.
The table sums up the rule. Read each row as: "when the constructor does this, new gives back that."
| What the constructor does | What new gives back | Why |
|---|---|---|
It has no return line. | The object new made. | new User("Ada") gives back the object that this pointed to. |
It returns a primitive, like "ignored" or 0. | The object new made. | new ignores the primitive and keeps its own object. |
| It returns an object. | That returned object. | return { name: "replacement" } takes the place of the object new made. |
| It returns a function. | That returned function. | Functions are objects too, so a returned function also takes the place of the new object. |
You will rarely need this rule in your own constructors. But it helps you read unusual library code and answer quiz puzzles. A quick check:
- The constructor returns an object or a function: you get that instead.
- It returns a number, string, boolean,
null, orundefined: you get the objectnewmade.
Built-in constructors and wrapper traps
SORTJavaScript comes with its own constructors too. They are called built-in constructors. For example:
new Date()makes a Date object for the current date and time.new Map()makes a Map: a collection of keys and values. A later lesson covers it.
But three built-ins are traps when you use them with new: new Number(5), new String("x"), and new Boolean(false). Each one makes a wrapper object: an object with a primitive value inside it. Code that expects a plain number, string, or boolean can then go wrong.
console.log(typeof new String("x"));
console.log(Boolean(new Boolean(false)));
console.log(typeof "x");
console.log(Boolean(false));- Line 1 prints
object, notstring. - Line 2 prints
true, even though the wrapper holdsfalse. - Lines 3 and 4 show the plain values for comparison. They print
stringandfalse.
The worst trap is new Boolean(false). The value inside is false. But the wrapper itself is an object, and every object counts as true in an if. Values that count as true are called truthy. So if (new Boolean(false)) runs its block.
Use plain values instead: 5, "x", and false. To convert a value, call Number(value), String(value), or Boolean(value) without new. Called that way, they give back plain values.
console.log(String(5));
console.log(Boolean(""));
console.log(typeof String(5));Line 1 turns the number 5 into the string "5". Line 2 prints false, because an empty string counts as false. Line 3 prints string: the result is a plain string, not an object.
- A Date object for the current time:
new Date() - A Map collection:
new Map() - Your own
Userconstructor - Turn text into a number:
Number("5") - Make an array from a list of items:
Array.from(items) - A text value:
new String("x") - A true/false value:
new Boolean(false) - One small record:
{ name: "Ada" }
Sort each case by how you should write it in everyday JavaScript. Each explanation points out the trap.
Where you’ll use this
You will meet constructors in three places:
- Older code and some libraries still use constructor functions.
- Built-in objects such as
Date,Map,Set,URL, and many browser objects are made withnew. - Classes use the same steps, with neater syntax.
function CartItem(name, price, quantity) {
if (!new.target) return new CartItem(name, price, quantity);
this.name = name;
this.price = price;
this.quantity = quantity;
}
CartItem.prototype.total = function () {
return this.price * this.quantity;
};
const apples = new CartItem("Apples", 2, 3);
console.log(apples.total());- Line 2 is a
new.targetguard. If someone forgetsnew, it calls the constructor again withnew. - Lines 3 to 5 store the item's name, price, and quantity on
this. - Lines 8 to 10 put one shared
totalmethod on the prototype. It multiplies the price by the quantity. - Line 12 makes 3 apples at 2 each. Line 13 prints
6.
Constructors aren't the only way to make objects. Object literals, functions that return objects (called factory functions), and classes are all useful too.
Constructors are still worth learning. They explain the common new Something() code, and they prepare you for prototypes and classes.
Common misconceptions
“A constructor is a special kind of declaration.”
No. It is usually a normal function. What changes is the call: with new, JavaScript makes an object and runs the function on it.
“The capital letter makes new work.”
No. The capital letter is only a habit that helps people read the code. JavaScript doesn't check it.
“new copies prototype methods into every object.”
No. Nothing is copied. new links the object to Constructor.prototype. When a method isn't on the object itself, JavaScript follows that link to find it.
“Returning anything from a constructor replaces the new object.”
No. Only a returned object or function takes its place. A returned primitive, like a number or a string, is ignored.
“new Boolean(false) behaves like false.”
No. It is an object, and every object is truthy, so it counts as true in an if. Don't use wrapper constructors for plain values.
When a constructor puzzle confuses you, ask two questions. Did the call use new? Did the constructor return an object? Those two answers predict most results.
Practice: constructors and new
5 EXERCISESWrite a Book constructor that stores a title and an author. Then make one book and print it.
function Book(title, author) {
this.title = title;
this.author = author;
}
const dune = new Book("Dune", "Frank Herbert");
console.log(dune.title + " by " + dune.author);function Book(title, author) {
this.title = title;
this.author = author;
}
const dune = new Book("Dune", "Frank Herbert");
console.log(dune.title + " by " + dune.author);new Book(...) makes a new object and runs Book with this set to it. The constructor writes title and author onto it, so the last line prints Dune by Frank Herbert.
Predict the two lines this prints. The first uses === to ask "same object?". The second uses instanceof to ask "made by Pet?".
function Pet(name) {
this.name = name;
}
const a = new Pet("Mochi");
const b = new Pet("Mochi");
console.log(a === b);
console.log(a instanceof Pet);The first line prints false. a and b are two different objects, even though their values match. The second line prints true, because a was made by new Pet(...).
A teammate wrote Task("Ship lesson") without new. Add a guard that fixes the call.
function Task(title) {
if (!new.target) return new Task(title);
this.title = title;
}
const task = Task("Ship lesson");
console.log(task.title);function Task(title) {
if (!new.target) return new Task(title);
this.title = title;
}
const task = Task("Ship lesson");
console.log(task.title);The guard sees a call without new and calls the constructor again with new. So Task("Ship lesson") still gives back a Task object, and the code prints Ship lesson.
Write an Account constructor that throws a helpful TypeError when someone forgets new.
function Account(name) {
if (!new.target) {
throw new TypeError("Use new with Account");
}
this.name = name;
}
console.log(new Account("Ada").name);function Account(name) {
if (!new.target) {
throw new TypeError("Use new with Account");
}
this.name = name;
}
console.log(new Account("Ada").name);This version stops right away when new is missing. A plain Account("Ada") would throw TypeError: Use new with Account. The call with new prints Ada.
Predict what this prints. The constructor writes to one object but returns another.
function Ticket() {
this.code = "A1";
return { code: "VIP" };
}
console.log(new Ticket().code);new Ticket() first makes an object with code: "A1". But the constructor then returns a different object, and that one wins. So the code prints VIP.
Quiz: check your understanding
7 QUESTIONSRead each snippet carefully. Your first try counts, and every choice explains why it is right or wrong.
Question 1 of 7How do people usually name constructor functions?
Choose an answer to see the explanation.
Question 2 of 7What does the User constructor print?
Read the code, then predictfunction User(name) { this.name = name; } const ada = new User("Ada"); console.log(ada.name);Choose an answer to see the explanation.
Question 3 of 7Which step is not part of what
new User("Ada")does?Choose an answer to see the explanation.
Question 4 of 7This constructor returns a string. What does it print?
Read the code, then predictfunction Box() { this.label = "fresh"; return "ignored"; } console.log(new Box().label);Choose an answer to see the explanation.
Question 5 of 7This constructor returns an object. What does it print?
Read the code, then predictfunction Box() { this.label = "fresh"; return { label: "replacement" }; } console.log(new Box().label);Choose an answer to see the explanation.
Question 6 of 7Inside a function written with
function, what isnew.targetwhen you call it withoutnew?Choose an answer to see the explanation.
Question 7 of 7What do these wrapper objects print?
Read the code, then predictconsole.log(typeof new String("x")); console.log(Boolean(new Boolean(false)));Choose an answer to see the explanation.
Key takeaways
- A constructor function is a normal function that you call with
new. By habit, its name starts with a capital letter. newmakes an empty object, links it toConstructor.prototype, runs the function withthisset to that object, and gives the object back.new.targetisundefinedwhen you forgetnew, and the function itself when you usenew. A constructor can use it to catch the mistake.- If a constructor returns an object, you get that object instead. If it returns a primitive,
newignores it. - Put shared methods on the prototype (or in a class, later). Don't use
newwithNumber,String, orBoolean.
Remember the one-liner.
A constructor plus new is a recipe you can reuse to make many separate objects with the same shape.
Up next: Object.keys, values & entries.