1

I am trying to wrap my head around prototypes, and wold like to know what the prototype is exactly. Much of the confusion stems from not understanding the metalanguage used to describe prototypes.

Here is what I know:

When we create a named constructor function with properties inside it, the properties inside the body of that constructor function are inherited by object instances created by that constructor function. Here I've created an instance called person001 from the constructor function named Person.

function Person(firstName,lastName) {
    this.firstName = firstName;
    this.lastName = lastName
}
undefined
var person001 = new Person("John","Doe");

When I look at the object instance in the console and trace the prototype chain, I find it in 2 different places. It is a constructor object of the dunder proto object...

Person {firstName: "John", lastName: "Doe"}
firstName: "John"
lastName: "Doe"
__proto__:
constructor: ƒ Person(firstName,lastName)
__proto__: Object

and a property of the prototype object inside of the same constructor object.

Person {firstName: "John", lastName: "Doe"}
firstName: "John"
lastName: "Doe"
__proto__:
constructor: ƒ Person(firstName,lastName)
arguments: null
caller: null
length: 2
name: "Person"
prototype: 
constructor: ƒ Person(firstName,lastName)
__proto__: Object
__proto__: ƒ ()
[[FunctionLocation]]: script.js:76
[[Scopes]]: Scopes[1]
__proto__: Object

When I add a property using the .prototype property of the named constructor function, I am adding that property to the prototype object, NOT to the constructor function. The added property will sit alongside the constructor function in the object of the prototype property. Here I add a property called age using the prototype property of the constructor function Person.

Person.prototype.age = 0;  

So now that I've added an additional property, what exactly is the prototype?

When I run the Object.getPrototypeOf method on the object instance person001 it returns what looks to me like the prototype object. It has 3 properties -- a constructor function, the property I added, and the implicit dunder proto object.

Object.getPrototypeOf(person001);
{age: 0, constructor: ƒ}
age: 0
constructor: ƒ Person(firstName,lastName)
__proto__: Object 

So what is the prototype? Is it the prototype object {constructor function, additional properties}? Or is it just the constructor function of the prototype object?

Thanks in advance for your assistance.

efw
  • 449
  • 3
  • 16

4 Answers4

2

When you do obj = new Person, there are three players in the game:

  • the freshly created object obj
  • the constructor function Person
  • the prototype, a special hidden object stored under Person.prototype

The relationships between them are as follows:

obj.__proto__ === Person.prototype

Person.prototype.constructor === Person

Illustration:

function Person(firstName,lastName) {
    this.firstName = firstName;
    this.lastName = lastName
}

var person1 = new Person("John","Doe");
var person2 = new Person("Ann","Smith");

Person.prototype.age = 42;

enter image description here

georg
  • 211,518
  • 52
  • 313
  • 390
  • This is a brilliant illustration. So referring to the illustration, it is now my understanding that 'The' prototype in this case is the Person object and all of the properties that belong to it, which include the constructor function object. When you add a property to the Person object using Person.prototype.age, the property is saved as an object within the constructor function's prototype object, and set as a property in the Person object. – efw Oct 16 '19 at 17:11
  • Georg, would you mind telling me what application you used to create the graphic? – efw Oct 16 '19 at 17:18
  • Putting it at the top of my bookmarks. Absolute genius. Thanks, Georg! – efw Oct 16 '19 at 18:35
0

Said you have created a constructor for a Person and then two instances of it:

const Person = function(name) {
  this.name = name;
  this.speak = () => console.log('My name is ' + this.name)
};
const john = new Person('John');
const mary = new Person('Mary');

john.speak();
mary.speak();

Now each person you create has a different name, and they can both speak it because they share the same property from some parent object.

But Mary not only can speak. She can sing. But John cannot.

        const Person = function(name) {
          this.name = name;
          this.speak = () => console.log('My name is ' + this.name)
        };
        const john = new Person('John');
        const mary = new Person('Mary');

        john.speak();
        mary.speak();
        
        mary.sing = () => console.log('♪♪♪ Lalalalalala ♪♪♪');
        
        mary.sing();
        john.sing(); // John is such a bad singer, that this throws an error !

What if later on you realize that your people do not only need to speak, but also to walk. You need a reference to their common parent in order to tell them to walk all in one line. That's the prototype. Both Mary and John share a common prototype and have, deep inside, a reference to that prototype (that's __proto__, for friends and family).

const Person = function(name) {
      this.name = name;
      this.speak = () => console.log('My name is ' + this.name)
    };
    const john = new Person('John');
    const mary = new Person('Mary');

    john.speak();
    mary.speak();
    
    Person.prototype.walk = () => console.log('I am walking alright');
    
    john.walk();
    mary.walk();
    
    // That is the same as:
    john.__proto__.walk()
    mary.__proto__.walk()

Now John has a bad fall, and it has troubles walking

    const Person = function(name) {
          this.name = name;
          this.speak = () => console.log('My name is ' + this.name)
        };
        const john = new Person('John');
        const mary = new Person('Mary');

        john.speak();
        mary.speak();
        
        Person.prototype.walk = () => console.log('I am walking alright');
        
        // John's infamous accident
        john.walk = () => console.log('My leg hurts so bad...');
        
        john.walk();
        mary.walk();
        
        

Instance has its own property, we use that.
It has not, we look in its __proto__, and use it if it exists.

Hope this helps!

giuseppedeponte
  • 2,366
  • 1
  • 9
  • 19
0

First of all, prototype is just an object. Almost every object in JS (except for eg. when you use Object.create(null)) has some prototype and the prototypes can be chained.

Example: When you create an array by using literal [], your array instance is connected to object (also array instance btw.) defining array's properties like map etc., which itself is connected to another prototype (object instance) defining properties of objects like toString. Those properties are usually functions. Now when you access one of the properties of your object like [].hasOwnProperty, the engine looks up the prototype chain to find it. It start at your [] instance, doesn't find it, continues to the prototype (the array), also without success, so moves to the last prototype (object instance) where it is finally found.

As you can see, prototype chain always ends somewhere, so if you try to retrieve prototype on the last prototype in the chain, you will get null.

Now back to the constructor functions. First thing to note is that those are just normal functions - the actual "creator" of the instance is the keyword new. Now every function has a prototype property which tells you this: Every object created by using this function will get prototype-connected to whatever is in the function's prototype property. By default, every function contains instance of Object in this property, which means that every object you create from this function will be prototype-connected to this instance and will therefore "inherit" properties like toString.

You can see the connection between function's prototype property and instance's prototype in this example.

function A() {}
var a = new A();
a.__proto__ == A.prototype; // is true

Last, the constructor property in the function's prototype property tells you, which function will be used to create new instances when using the function with the new keyword. This mouthful boils down to:

function A() {}
A == A.prototype.constructor; // is true

It is a reference to itself. This property was optional to set when creating your own inheritance chain prior to ES6, but was done nevertheless for correctness' sake.

So what is prototype? It is just an object, connected to other object instances via a special prototype connection, giving it access to predefined properties. It is a way to do inheritance in JS.

Dan Macak
  • 16,109
  • 3
  • 26
  • 43
  • No it doesn't, consider this `Object.hasOwnProperty([], hasOwnProperty)` returns false, but `[].hasOwnProperty` gives you function back. – Dan Macak Oct 16 '19 at 08:32
  • Ah, yes it does, but your example wasn't calling `.hasOwnProperty`. Removing the comment. – Teemu Oct 16 '19 at 08:55
  • We were talking apples and oranges. Now I see that you were commenting on the fn call - yes it obviously checks just the instance. But regarding the property access, it goes up the prototype chain. – Dan Macak Oct 16 '19 at 09:03
  • Thank you, Dan. This is very helpful. – efw Oct 16 '19 at 16:34
0

EDIT: My friend @teemu added to my answer: Every inbuilt object has its prototype.

In very simple language, Every function is a special kind of object in javascript And every function has its own "prototype" object container. So every constructor function will have its own "prototype" object, which will be shared with all objects in __proto__ constructed using this. Let's see in example:

// constructor function:
    var Person = function(name, age){
      this.name = name;
      this.age = age
    }

var tony = new Person('tony', 21);
var bruce = new Person('bruce', 22);

so as we discussed,

Person.prototype should be the same as tony.__proto__ and bruce.__proto__

note: you can replace Person with inbuilt Array, Object or String also.

to verify this we can do this:

Person.prototype == tony.__proto__;  //true
Person.prototype == bruce.__proto__; //true
bruce.__proto__ == tony.__proto__;  //true

next thing we are going to do add one property on bruce.__proto__:

bruce.__proto__.isSuperHero = true;

it is same thing when you add properties in prototype

but this will be reflected everywhere;

console.log(tony.__proto__.isSuperHero )  // true
console.log(Person.prototype.isSuperHero)   //true

Now you can imagine a prototype as a common space like a house for all family members if any of the family members make changes in that house it will be changed for everyone and every family member can access the house no matter how old he is or he is just a newborn. it will be the same for every family members, stairs will be same, the rooms will be same, the color of the walls will be same.

I hope this will help you in understanding prototype chaining, I tried to explain it in different way which is a better way in my view.

Sheelpriy
  • 1,675
  • 17
  • 28
  • Not just functions, every object in JS has the prototype (except those specifically created without it). – Teemu Oct 16 '19 at 07:53