Can someone please help me understand the prototype property? I don't understand whether the prototype property is a property of the function or of what's inside the function.
Let's say we create the following constructor, Food. At this point, the function Food() has the property Food.prototype. Since Food is an instance of Object, then this means that Obect.prototype is the prototype attribute of all objects created with Food().
function Food() {}
Then create another constructor Pizza. Pizza has the property Pizza.prototype.
function Pizza(toppings) {
this.toppings = toppings;
}
Then we make Pizza inherit from Food by setting Pizza's prototype property to be an instance of Food. The prototype attribute of Pizza is now Food.prototype since Food is the parent of pizza.
Pizza.prototype = new Food();
Then we create an instance of Pizza
var myPizza = new Pizza("pepperoni");
Does myPizza also have a prototype property that it inherits from Pizza? If so, is myPizza.prototype == Object.prototype? What is Obejct.prototype? Is it a property of Object()? Do only functions have the prototype property? Is Object.prototype an object? Does Pizza.prototype refer to the entire function that creates the Pizza constructor? Is this function an object itself?
function Pizza(toppings) {
this.toppings = toppings;
}
Or does the Pizza.prototype just refer to what is inside the scope of Pizza()?
this.toppings = toppings;
Is Pizza.toppings a property of Pizza.prototype? But isn't Pizza.prototype a property of Pizza()? Is toppings only a property of an object created with the Pizza constructor? And Pizza.prototype is a property of the Pizza constructor?
Current prototype chain is as follows:
myPizza --> Pizza.prototype --> Food.prototype --> Object.prototype
Instead of starting with answers to the questions, I’m going to start with the way prototypes and constructors work to avoid confusion in trying to interpret those answers with partial understanding. So, prototype recap:
Every value in JavaScript, except null
and undefined
, has an associated value: its prototype.¹
The prototype is used to look up properties. When you evaluate x.foo
, you check to see if the value x
has an own property – a property on itself – named “foo”. If it does, x.foo
is the value of that own property. If not, the lookup continues on x
’s prototype.
A value’s prototype can be null
, meaning any property lookup that didn’t find an own property results in undefined
.
You can get a value’s prototype with the function Object.getPrototypeOf
.
You can create a new object with a specific prototype with the function Object.create
.
Constructors in JavaScript have a property named “prototype”. The value of this property isn’t the prototype of the constructor; it’s the prototype of values created with the constructor. Taking your example constructor:
function Food() {}
If you run new Food()
, a new object will be created with its prototype set to Food.prototype
, and Food
will be executed with this
set to that new object. In other words, this:
// create a new instance of Food
let f = new Food();
means the same thing as this:
// create a new object with Food.prototype as its prototype
let f = Object.create(Food.prototype);
// initialize it using the constructor
Food.call(f);
Now, the way property lookups work, summarized above, gives rise to a prototype chain. If x
has a prototype y
and y
has no prototype, x.foo
gets looked up on this chain:
x -> y -> null
x
has an own property “foo”, x.foo
evaluates to its valuey
has an own property “foo”, x.foo
evaluates to its valuenull
, the end of the chain, so x.foo
is undefined
The default value of the prototype
property of constructors is a new Object
instance, so the prototype chain of a new Food()
looks like this:
f -> Food.prototype -> Object.prototype -> null
and you can say that a value x
is an instance of a constructor C
if x
’s prototype is C.prototype
or x
’s prototype is not null and an instance of C
. (If x
is C.prototype
, x
is not an instance of C
.) This is how the instanceof
operator works²:
console.log({} instanceof Object); // true
console.log(Object.prototype instanceof Object); // false
You can also say that C
inherits from D
if C.prototype
is an instance of D
.
Everything built into JavaScript has Object.prototype
on its prototype chain. Functions are Object
instances:
function f() {}
f instanceof Object // true
and so constructors are too:
function Food() {}
Food instanceof Object // true
It’s important to note that this doesn’t say anything about the relationship between instances of Food
and Object
. You can set Food.prototype = null
to get new Food() instanceof Object === false
, but it will still be the case that Food instanceof Object
.
I hope that this framework is enough to address your questions. That was the idea, anyway. Still going to respond to them explicitly using it:
Let's say we create the following constructor, Food. At this point, the function Food() has the property Food.prototype. Since Food is an instance of Object, then this means that Obect.prototype is the prototype attribute of all objects created with Food().
All objects created with new Food()
have a prototype of Food.prototype
. The prototype of Food.prototype
is Object.prototype
. Food
is a function, which means it’s true that it’s an instance of Object
, but the relevant instance of Object
here is Food.prototype
.
Then we make Pizza inherit from Food by setting Pizza's prototype property to be an instance of Food. The prototype attribute of Pizza is now Food.prototype since Food is the parent of pizza.
The prototype property of the function Pizza
is now an object with Food.prototype
as its prototype.
Does myPizza also have a prototype property that it inherits from Pizza?
myPizza
doesn’t inherit anything from Pizza
. It inherits everything from the object Pizza.prototype
. Since Pizza.prototype
does not have a property named “prototype”, myPizza
does not inherit a property named “prototype”.
What is Object.prototype? Is it a property of Object()?
Object.prototype
literally means “the ‘prototype’ property of Object
”, so yes. This value is on the prototype chain of all instances of Object, which is only significant because most things in JavaScript are instances of Object. Apart from that, it’s like any other prototype
property of a constructor.
Do only functions have the prototype property?
Functions defined using the function
or class
keywords start with a property named prototype
. (Arrow functions don’t, and can’t be used as constructors.) You can put a property named prototype
on anything. It only has meaning when it’s on a constructor – a function used with new
or instanceof
.
Is Object.prototype an object?
It’s an object with a lowercase “o”, when using “object” to refer to any non-primitive² value. It is not an instance of Object
– it has a null
prototype.
Is this function an object itself?
Yes, functions are objects³.
Does Pizza.prototype refer to the entire function that creates the Pizza constructor?
No. Pizza.prototype
is not a function. It’s used by the Pizza
constructor but the Pizza
constructor is not an instance of it and was not created by it.
Or does the Pizza.prototype just refer to what is inside the scope of Pizza()?
Nothing to do with scope, either. When you evaluate new Pizza()
, Pizza
is called with a new instance of Pizza
as its this
value. this
is not the scope of a function. A “scope” is an area where some set of variables is visible.
function Foo() {
let x = 5; // a variable in scope. unrelated to `this`.
}
function Foo() {
this.x = 5; // assigning to a property of the value `this`.
// unrelated to variables.
}
Is Pizza.toppings a property of Pizza.prototype?
It’s not Pizza.toppings
. There’s a new object – this
– and you’re assigning the value of a parameter to the Pizza
function, named toppings
, to a property of that new object, also named toppings
. The new object’s prototype is Pizza.prototype
, but the new object is not Pizza.prototype
itself and so the answer is that “no”, toppings
is not a property of Pizza.prototype
.
But isn't Pizza.prototype a property of Pizza()?
It’s a property of Pizza
. (Just making sure Pizza()
refers to the function and not the value you get by calling the function. Gotta be precise!)
Is toppings only a property of an object created with the Pizza constructor?
Right!
And Pizza.prototype is a property of the Pizza constructor?
Yes.
Current prototype chain is as follows:
myPizza --> Pizza.prototype --> Food.prototype --> Object.prototype
Also correct. You can confirm it with the aforementioned getPrototypeOf
.
Object.prototype.toString = function () { return 'Object.prototype'; };
function Food() {}
Food.prototype.toString = function () { return 'Food.prototype'; };
function Pizza(toppings) {
this.toppings = toppings;
}
Pizza.prototype = Object.create(Food.prototype);
Pizza.prototype.toString = function () { return 'Pizza.prototype'; };
let myPizza = new Pizza();
myPizza.toString = function () { return 'myPizza'; };
let chainLink = myPizza;
while (true) {
console.log(String(chainLink));
if (chainLink === null) {
break;
}
chainLink = Object.getPrototypeOf(chainLink);
}
Note that I’ve written Object.create(Food.prototype)
instead of new Food()
here. You don’t want to run the parent constructor outside the child constructor, though it was common in ES3. ES5 added Object.create
. ES6 added class
and extends
, which is what you’ll want to use in practice.
¹ Primitives don’t have a [[Prototype]] in the spec but that doesn’t really matter because their property lookup and post-ES5 Object.getPrototypeOf
work like they do.
² Primitives are strings, booleans, numbers, symbols, null
, and undefined
. Primitives are immutable – they don’t have any own properties. The distinction between objects and primitives isn’t that important in JavaScript, but because they don’t have any own properties it doesn’t make sense to use them as prototypes. They also don’t count as instanceof
anything for what I’m going to claim are historical reasons.
³ By default, that is. You can ask one or all of them not to be with Object.setPrototypeOf
. There’s no reason to do this.