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.prototypeDescribe the linknew F()gives each instance and how lookup uses it. - 02Share behavior deliberatelyMove methods from per-instance copies onto a constructor’s prototype.
- 03Build constructor inheritanceConnect child and parent constructors with
call,Object.create, andconstructor.
The template shelf behind constructor objects
The lesson titled The prototype chain showed that property lookup can climb from one object to another. This lesson answers the constructor version of the same question: when new User("Ada") creates an object, which object does Ada delegate to?
The answer is User.prototype. Every ordinary function that can be used with new owns a prototype property. That property is an object. When you call the function with new, the new instance gets a hidden [[Prototype]] link to that object.
Imagine a builder who makes many badges. Each badge gets its own name engraved on it, but they all use the same workshop shelf for tools like “print greeting.” Putting the tool on the shelf avoids attaching a separate copy to every badge.
- In real life: A workshop builder
- In JavaScript: The constructor function, such as
User - In real life: A shelf of shared tools
- In JavaScript:
User.prototype - In real life: A finished item with its own label
- In JavaScript: An instance, such as
ada, with own fields - In real life: Walking back to the shelf for a tool
- In JavaScript: Prototype lookup for
ada.greet
Where the analogy stops: A real shelf is a place in a room. A JavaScript prototype is an object reference. The important part is sharing: many instances can point at the same object.
F.prototype is the object that new F() assigns as the new instance’s prototype link.
You will use that fact to share methods, understand the inherited constructor property, replace a prototype safely, and set up inheritance between constructor functions. Class syntax in the next module builds on the same links; it just writes them for you.
F.prototype: the object new instances point to
STEP THROUGHStart with one constructor and one shared method. The constructor puts data on each instance. The prototype stores behavior that every instance can reuse.
Predict each console.log. Then step through the constructor, the prototype link, and the inherited 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);The key check is Object.getPrototypeOf(ada) === User.prototype. Object.getPrototypeOf is the standard way to inspect an object’s prototype link. You may also see __proto__ in articles or DevTools, but use Object.getPrototypeOf in code you write.
When ada.greet() runs, lookup first asks, “does Ada have her own greet property?” She does not. Lookup then climbs to User.prototype, finds the method, and calls it with this set to ada. That last part is why a shared method can still read each instance’s own name.
The constructor property is a label, not magic
INTERACTIVEA function’s default prototype object starts with one property: constructor, pointing back to the function. That is why ada.constructor === User is true in the first experiment. Ada does not own that property. Lookup finds it on User.prototype.
The constructor property is like a sticker on the shelf saying which builder it belongs to. If you swap the shelf, the old sticker does not automatically move.
- In real life: A label on the shelf saying “Built by User”
- In JavaScript:
User.prototype.constructor = User - In real life: A badge looking up at the shelf label
- In JavaScript:
ada.constructorfound by prototype lookup - In real life: Replacing the shelf with an unlabeled one
- In JavaScript:
User.prototype = { greet() {} }
Where the analogy stops: The label is useful metadata, not the mechanism that creates objects. new uses the function you called, not a constructor label found on an existing instance.
Switch the setting to restore or omit constructor, then step through which shelf 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 details matter in real projects. First, replacing User.prototype does not move existing instances: Ada keeps pointing at the old object. Second, object literals do not automatically include constructor: User. If code relies on instance.constructor, restore it when you replace the whole prototype object.
Inheriting between constructors
STEP THROUGHBefore class syntax, JavaScript code connected constructor functions directly. The classic pattern has three jobs:
- Inside the child constructor, call the parent constructor with
User.call(this, name)so own fields are initialized on the same new object. - Set
Admin.prototype = Object.create(User.prototype)so admin instances can find user methods by lookup. - Restore
Admin.prototype.constructor = Admin, then add or override child methods.
Classic constructor inheritance is two jobs: call the parent constructor for own fields, and link the child prototype to the parent prototype for 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);Overriding means the child prototype defines a property with the same name as the parent method. Lookup finds Admin.prototype.greet before User.prototype.greet. Calling User.prototype.greet.call(this) deliberately reuses the parent method with the same instance as this.
Classes set up the same prototype links
COMPAREClasses come next module, so do not worry about mastering their syntax yet. The important bridge is that class methods also live on the class’s prototype, and extends links the child prototype to the parent 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);The class version prints the same greeting as the constructor version, and Object.getPrototypeOf(Admin.prototype) === User.prototype is still true. super(name) fills the role of User.call(this, name); super.greet() fills the role of User.prototype.greet.call(this).
.prototype?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);Ordinary functions and classes have a prototype property because they can be construction targets. Arrow functions cannot be used with new, so their prototype is undefined. Methods written with object or class method syntax also are not constructors.
Where you’ll use this
You will meet constructor prototypes whenever you read older libraries, inspect objects in DevTools, debug instanceof, or design objects that share behavior. Even modern class code is easier to debug when you know the prototype chain it creates.
- Put per-object data, such as
name, on the instance. - Put reusable methods, such as
greet, on the prototype. - When replacing a prototype object, restore
constructorif code will read it. - For inheritance, initialize own fields and link prototypes; you need both.
Common misconceptions
- “
F.prototypeis the same as an instance’s prototype.” It is the object future instances link to. The instance’s link points at it. - “Prototype methods are copied into every object.” They are shared by lookup. Identity checks prove it.
- “
constructorcreates the object.” It is just a property, usually inherited from the prototype. - “Replacing
F.prototypeupdates old objects.” Old objects keep the old linked object. - “Calling the parent constructor sets up method inheritance.” It initializes fields only;
Object.createsets the method chain. - “Classes are a different inheritance system.” They are cleaner syntax over prototype links.
Practice exercises
5 EXERCISESRun the code mentally first. Then type the two logged 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 log is true because Ada points at User.prototype. The second is false because greet is shared on the prototype, not owned by Ada.
Rewrite the constructor 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.
Repair the replacement prototype so instances find User through 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);Replacing the prototype removed the default label. Adding constructor: User makes ada.constructor === User true again.
Complete the classic pattern 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);The parent constructor initializes name; Object.create links methods; restoring constructor fixes the label.
Write the equivalent class User and 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 writes the same own name field, and the class method still lives 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 the shared method comparison 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 7What is usually best for methods shared by many instances?
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.- Own fields belong on each instance; shared methods belong on the prototype.
constructoris normally inherited from the prototype and must be restored after replacing it.- Constructor inheritance needs both parent initialization and prototype linking.
- Class syntax creates the same kind of prototype relationships with clearer syntax.
Final definition: F.prototype is the shared object that new F() places in each instance’s prototype chain.
Up next: Built-in prototypes.