F.prototype & constructors
Learn how new links objects to a constructor’s prototype, why the constructor property matters, how prototype methods are shared, and how constructor inheritance works.
- 01Explain
F.prototypeSay which objectnew F()links each new object to, and how JavaScript uses that link to find methods. - 02Share one methodMove a method out of the constructor and onto
F.prototype, so every object uses the same function. - 03Make one constructor build on anotherConnect
AdmintoUserwithUser.call,Object.create, and theconstructorproperty.
Where new objects find their shared methods
In the lesson The prototype chain, you saw that every object has a hidden link to one other object. That other object is its prototype. When you read a property the object doesn't have, JavaScript follows the link and looks in the prototype.
This lesson asks one question. When new User("Ada") makes a new object, which object does its hidden link point to?
The answer is User.prototype. Here is how it works:
- A function written with the
functionkeyword has a property calledprototype. - That property holds a plain object.
- When you call the function with
new, the new object's hidden link points to that object.
A function you call with new is called a constructor. An object it makes is called an instance. So new User("Ada") makes an instance of User.
A builder makes many name badges. Each badge has its own name on it. But all the badges use the same tools, such as a "print a greeting" tool. The builder keeps one copy of each tool on a shelf. Nobody glues a copy of every tool onto every badge.
- In real life: The person who builds badges
- In JavaScript: The constructor function, such as
User - In real life: One shelf of tools that every badge uses
- In JavaScript:
User.prototype - In real life: A finished badge with its own name on it
- In JavaScript: An instance, such as
ada, with its ownname - In real life: Walking back to the shelf to get a tool
- In JavaScript: JavaScript finding
ada.greetonUser.prototype
F.prototype is the object that every new F() instance gets as its prototype.
In this lesson you will use that fact to:
- share one method between many objects,
- understand the
constructorproperty, - replace
F.prototypewithout surprises, - make one constructor build on another one.
The class syntax in the next module makes the same links. It just writes them for you.
F.prototype: the object new instances link to
STEP THROUGHStart with one constructor and one method. The constructor puts data, the name, on each new object. User.prototype holds a method, greet, that every instance can use.
Guess what each console.log prints. Then step through the constructor, the hidden link, and the constructor property.
script
this.name = name;} User.prototype.greet = function () { return "Hi, " + this.name;}; const ada = new User("Ada");console.log(Object.getPrototypeOf(ada) === User.prototype);console.log(ada.greet());console.log(ada.constructor === User);Line 10 is the key check: Object.getPrototypeOf(ada) === User.prototype. Object.getPrototypeOf(obj) gives you the object that obj's hidden link points to. You may also see __proto__ in old articles or in DevTools. In your own code, use Object.getPrototypeOf.
new User("Ada") gives ada her own name. It also links ada to User.prototype, where the shared greet method is stored.Here is what happens on line 11, when ada.greet() runs:
- JavaScript asks: does
adahave agreetproperty of her own? She doesn't. - It follows the hidden link to
User.prototypeand findsgreetthere. - It calls
greetwiththisset toada, becauseadais before the dot.
That last step is why one shared method can still read each instance's own name.
The constructor property points back to the function
INTERACTIVEWhen JavaScript makes a function, it also makes its prototype object. That object starts with one property, constructor, which points back to the function. So User.prototype.constructor is User.
That is why line 12 of the first example printed true for ada.constructor === User. ada doesn't have her own constructor property. JavaScript finds it on User.prototype.
The shelf has a label that says which builder it belongs to. If you take the shelf away and put up a new one, the old label does not move to the new shelf by itself.
- In real life: A label on the shelf that says “Built by User”
- In JavaScript:
User.prototype.constructor = User - In real life: A badge looking up at the shelf label
- In JavaScript: JavaScript finding
ada.constructoronUser.prototype - In real life: Putting up a new shelf with no label
- In JavaScript:
User.prototype = { greet() {} }
function Product(name) { this.name = name;}console.log(Product.prototype.constructor === Product);Line 4 prints true. The constructor property is useful to look at, but it is a normal property. It does not build objects, and your code can change it.
Now see what happens when you put a whole new object in User.prototype. Choose whether the new object has constructor: User, then step through.
Choose whether the new object has constructor, then step through to see which object each instance uses.
script
function User(name) { this.name = name;} return "Old shelf: " + this.name;};const ada = new User("Ada"); User.prototype = { greet() { return "New shelf: " + this.name; },};const lin = new User("Lin"); console.log(ada.greet());console.log(lin.greet());console.log(ada.constructor === User);console.log(lin.constructor === User);Two things to remember from this experiment:
- Putting a new object in
User.prototypedoes not change objects you already made.adastill links to the old object. - An object you write with
{ }does not getconstructor: Userby itself. If your code readsinstance.constructor, add it back when you replace the prototype.
function User(name) { this.name = name;}User.prototype.greet = function () { return "Hi, " + this.name;};const ada = new User("Ada");User.prototype.wave = function () { return "Wave, " + this.name;};console.log(ada.wave());console.log(ada.constructor === User);User.prototype = { greet() { return "Hello, " + this.name; },};const lin = new User("Lin");console.log(ada.greet());console.log(lin.greet());console.log(lin.constructor === User);Line by line:
- Line 8 adds
waveto the object that is already inUser.prototype.adalinks to that object, so she can usewavetoo. - Line 12 prints
true. Adding a method kept theconstructorproperty. - Line 13 puts a brand-new object in
User.prototype, with a newgreet. - Line 19 prints
Hi, Ada.adastill uses the old object. - Line 20 prints
Hello, Lin.linwas made after line 13, so he uses the new object. - Line 21 prints
false. The new object has noconstructor, so JavaScript keeps looking up the chain. It findsObject.prototype.constructor, which isObject.
function Product(name) { this.name = name;}Product.prototype = { describe() { return this.name; },};Object.defineProperty(Product.prototype, "constructor", { value: Product, writable: true, configurable: true,});console.log(Product.prototype.constructor === Product);console.log(Object.keys(Product.prototype).includes("constructor"));Lines 9 to 13 use Object.defineProperty. It adds a property and lets you set its settings. One setting is enumerable: whether Object.keys and for...in list the property. You didn't set it here, so it is false.
That matches the original constructor property, which is not listed either. Line 14 prints true, and line 15 prints false.
function User(name) { this.name = name;}const ada = new User("Ada");const oldShelf = Object.getPrototypeOf(ada);User.prototype = {};const lin = new User("Lin");console.log(Object.getPrototypeOf(ada) === oldShelf);console.log(Object.getPrototypeOf(lin) === User.prototype);Line 5 saves the object that ada links to as oldShelf. Line 6 puts a new object in User.prototype. That changes the function's property, but it cannot change the hidden link already stored in ada. Both lines 8 and 9 print true.
ada was made before line 6, so she still links to the old object. Line 6 points User.prototype at a new object, and only lin links to it.function User(name) { this.name = name;}const ada = new User("Ada");console.log(ada instanceof User);User.prototype = {};const lin = new User("Lin");console.log(ada instanceof User);console.log(lin instanceof User);ada instanceof User asks: is the object in User.prototype right now somewhere in ada's chain of links?
- Line 5 prints
true. Nothing has been replaced yet. - Line 8 prints
false.adastill links to the old object, andUser.prototypeis now a different one. - Line 9 prints
true.linlinks to the new object.
One constructor building on another
STEP THROUGHSometimes you want a more specific kind of object. An Admin is a User with a level. Admins should get everything users have. This is called inheritance.
Before class existed, code connected two constructors by hand. The usual pattern has three jobs:
- Inside
Admin, runUser.call(this, name)..call(this, ...)runs a function withthisset to the object you pass. SoUserwritesnameonto the new admin. - Set
Admin.prototype = Object.create(User.prototype).Object.create(obj)makes a new empty object whose hidden link points toobj. Now admins can find user methods. - Set
Admin.prototype.constructor = Admin, because the new object has noconstructorof its own. Then add the admin's own methods.
Building one constructor on another takes two jobs: call the parent constructor to set the data, and link the child prototype to the parent prototype to share the methods.
script
this.name = name;}User.prototype.greet = function () { return "Hi, " + this.name;}; function Admin(name, level) { User.call(this, name); this.level = level;}Admin.prototype = Object.create(User.prototype);Admin.prototype.constructor = Admin;Admin.prototype.greet = function () { return User.prototype.greet.call(this) + " (admin " + this.level + ")";}; const maya = new Admin("Maya", 2);console.log(maya.greet());console.log(maya instanceof Admin);console.log(maya instanceof User);Line 14 puts a new greet on Admin.prototype. It has the same name as the method on User.prototype. This is called overriding: JavaScript finds Admin.prototype.greet first, so it never reaches the user one by itself.
On line 15, User.prototype.greet.call(this) runs the user method on purpose, with the same admin as this. Then it adds (admin 2).
Object.create links Admin.prototype to User.prototype. JavaScript finds Admin's greet first, and that greet calls User's greet.function User(name) { this.name = name;}function Admin(name) { User.call(this, name);}Admin.prototype = Object.create(User.prototype);Admin.prototype.constructor = Object;const maya = new Admin("Maya");console.log(maya instanceof Admin);console.log(maya instanceof User);console.log(maya.constructor === Object);Line 8 sets Admin.prototype.constructor to Object, which is wrong on purpose. Lines 10 and 11 still print true, because instanceof looks at the hidden links, and line 7 made those. Line 12 prints true too: maya.constructor is now Object.
So constructor is only a property you can read. The hidden links are what really connect the objects.
Classes make the same links
COMPAREYou will learn classes in the next module, so you don't need to learn their syntax yet. For now, know two things:
- Methods written in a class are stored on the class's
prototype. extendslinks the child's prototype to the parent's prototype.
class User { constructor(name) { this.name = name; } greet() { return "Hi, " + this.name; }} class Admin extends User { constructor(name, level) { super(name); this.level = level; } greet() { return super.greet() + " (admin " + this.level + ")"; }} const maya = new Admin("Maya", 2);console.log(maya.greet());console.log(Object.getPrototypeOf(Admin.prototype) === User.prototype);Line 21 prints the same greeting as the constructor version: Hi, Maya (admin 2). Line 22 prints true, so Admin.prototype links to User.prototype, just like before.
super(name)on line 12 does the job ofUser.call(this, name).super.greet()on line 16 does the job ofUser.prototype.greet.call(this).
console.log((function User() {}).prototype !== undefined);console.log((() => {}).prototype === undefined);const tools = { build() {} };console.log(tools.build.prototype === undefined);class Panel {}console.log(typeof Panel.prototype);- Line 1 prints
true. A function written withfunctionhas aprototype, because you can call it withnew. - Line 2 prints
true. An arrow function can't be called withnew, so itsprototypeisundefined. - Line 4 prints
true. A method written asbuild() {}inside an object can't be called withneweither. - Line 6 prints
object. A class has aprototypeobject.
Where you'll use this
You will meet constructor prototypes when you:
- read older libraries that use constructor functions,
- look at objects in DevTools and see their prototype,
- find out why
instanceofgives a surprising answer, - make many objects that share the same methods.
Class code is easier to debug too, once you know the links it makes.
- Put data that is different for each object, such as
name, on the instance. - Put methods every object can share, such as
greet, on the prototype. - If you replace a whole prototype object, add
constructorback if any code reads it. - For inheritance, do both jobs: call the parent constructor, and link the prototypes.
Common misconceptions
- “
F.prototypeis the prototype ofF.” No. It is the object that instances made bynew F()link to. - “Prototype methods are copied into every object.” No. Every object finds the same one by following its link.
ada.greet === lin.greetistrue. - “The
constructorproperty creates the object.” No. It is a normal property, and instances usually find it on the prototype. - “Replacing
F.prototypechanges old objects.” No. Old objects keep the object they already link to. - “Calling the parent constructor gives the child its methods.” No. It only sets the fields.
Object.createmakes the link to the methods. - “Classes use a different kind of inheritance.” No. Classes are a shorter way to write the same prototype links.
Practice exercises
5 EXERCISESRead the code and work out the answers first. Then type the two printed values.
function User(name) {
this.name = name;
}
User.prototype.greet = function () {
return "Hi " + this.name;
};
const ada = new User("Ada");
console.log(Object.getPrototypeOf(ada) === User.prototype);
console.log(ada.hasOwnProperty("greet"));The first line prints true, because ada links to User.prototype. The second prints false, because greet is stored on the prototype, not on ada.
Change the code so all users share one greet function.
function User(name) {
this.name = name;
this.greet = function () {
return "Hi, " + this.name;
};
}function User(name) {
this.name = name;
}
User.prototype.greet = function () {
return "Hi, " + this.name;
};
const ada = new User("Ada");
const lin = new User("Lin");
console.log(ada.greet === lin.greet);
console.log(ada.greet());Only the data stays in the constructor. The method moves to User.prototype, so ada.greet === lin.greet is true.
Fix the new prototype object so instances find User when they read constructor.
function User(name) {
this.name = name;
}
User.prototype = {
greet() {
return "Hi, " + this.name;
},
};function User(name) {
this.name = name;
}
User.prototype = {
constructor: User,
greet() {
return "Hi, " + this.name;
},
};
const ada = new User("Ada");
console.log(ada.constructor === User);The new object had no constructor property. Adding constructor: User makes ada.constructor === User print true again.
Finish the three jobs so an admin is also an instance of User.
function User(name) {
this.name = name;
}
User.prototype.greet = function () {
return "Hi, " + this.name;
};
function Admin(name, level) {
// initialize User's fields here
this.level = level;
}
// link Admin.prototype herefunction User(name) {
this.name = name;
}
User.prototype.greet = function () {
return "Hi, " + this.name;
};
function Admin(name, level) {
User.call(this, name);
this.level = level;
}
Admin.prototype = Object.create(User.prototype);
Admin.prototype.constructor = Admin;
const maya = new Admin("Maya", 2);
console.log(maya instanceof User);
console.log(maya.constructor === Admin);User.call(this, name) sets name. Object.create links admins to the user methods. Setting constructor puts the property back.
Write the same thing as class User, then check the greeting.
function User(name) {
this.name = name;
}
User.prototype.greet = function () {
return "Hi, " + this.name;
};class User {
constructor(name) {
this.name = name;
}
greet() {
return "Hi, " + this.name;
}
}
const ada = new User("Ada");
console.log(ada.greet());The class constructor sets the same name on each object, and the greet method is still stored on User.prototype.
Check your understanding
7 QUESTIONSQuestion 1 of 7What does
new User()do withUser.prototype?Choose an answer to see the explanation.
Question 2 of 7What does this code print?
Read the code, then predictfunction User(name) { this.name = name; } User.prototype.greet = function () { return "Hi " + this.name; }; const ada = new User("Ada"); const lin = new User("Lin"); console.log(ada.greet === lin.greet);Choose an answer to see the explanation.
Question 3 of 7After
User.prototype = { greet() {} }, why cannew User().constructor === Objectbe true?Choose an answer to see the explanation.
Question 4 of 7Which line connects
Admininstances toUser.prototypemethods?Read the code, then predictfunction Admin(name) { User.call(this, name); } Admin.prototype = Object.create(User.prototype); Admin.prototype.constructor = Admin;Choose an answer to see the explanation.
Question 5 of 7What does this print?
Read the code, then predictconsole.log((() => {}).prototype === undefined);Choose an answer to see the explanation.
Question 6 of 7Where should you usually put a method that many instances share?
Choose an answer to see the explanation.
Question 7 of 7How does
class Admin extends Userrelate to the constructor pattern?Choose an answer to see the explanation.
Key takeaways
new F()links the new object toF.prototype.- Data that is different for each object goes on the instance. Shared methods go on the prototype.
- Instances usually find
constructoron the prototype. Add it back after you replace the prototype object. - For one constructor to build on another, call the parent constructor and link the prototypes.
- Classes make the same prototype links with shorter code.
Final definition: F.prototype is the shared object that every new F() instance links to.
Up next: Built-in prototypes.