lukeaus
lukeaus

Reputation: 12255

Object.getPrototypeOf() vs .prototype

I am learning some JS and I am hoping someone can explain to me, in simplistic terms, the difference between Object.getPrototypeOf() vs .prototype

function ParentClass() {}

function ChildClass() {}

ChildClass.prototype = new ParentClass();

var mychild = new ChildClass();
var myparent = new ParentClass();


// .getPrototypeOf
Object.getPrototypeOf(ChildClass.prototype)   // ParentClass {}
Object.getPrototypeOf(mychild)                // ParentClass {}
Object.getPrototypeOf(ParentClass.prototype)  // {}
Object.getPrototypeOf(myparent)               // ParentClass {}

// .prototype
ParentClass.prototype                         // ParentClass {}
myparent.prototype                            // undefined
ChildClass.prototype                          // ParentClass {}
mychild.prototype                             // undefined

So it looks like you can only call .prototype on a constructor?

Are there any other differences?

Upvotes: 45

Views: 15835

Answers (6)

Joel
Joel

Reputation: 121

They are definitely different, but they're very similar in that they both simply retrieve prototypes. You can use Object.getPrototypeOf() and .prototype to get the exact same thing:

function Plant(type, size) {
  this.type = type,
  this.size = size
};

let plant1 = new Plant("grass", "small")

console.log(Plant.prototype === Object.getPrototypeOf(plant1)) // returns true

The difference is that .prototype will get the prototype of a specified constructor function's instances, while Object.getPrototypeOf() will get the prototype of the object specified inside the parentheses — whether that object is a constructor function or (from what I can tell) any other object.

In other words, when you do MyConstructor.prototype, you're not getting the prototype of MyConstructor, you're getting the prototype of MyConstructor's instances instead. If you want the prototype of MyConstructor, you can do

Object.getPrototypeOf(MyConstructor) // preferred, or 
MyConstructor.__proto__ // bad because MDN says so

Disclaimer: I'm still more or less new to JS and to programming in general. This explanation is just the result of my own attempts to understand this topic myself.

Upvotes: 6

Burrito
Burrito

Reputation: 1624

@lukeaus's answer is excellent. For me the most important points are:

  • A function's .prototype property is NOT the same as the function's prototype.
  • The .prototype property specifies the prototype of objects constructed from the function.
function MyConstructor() {} // automatically creates MyConstructor.prototype
// You can add methods to MyConstructor.prototype for objects to "inherit"
MyConstructor.prototype.foo = function() { console.log("do foo") }
// Or even reassign .prototype
// MyConstructor.prototype = { foo: function() { console.log("more foo?") } }
var obj = new MyConstructor()
Object.getPrototypeOf(obj) === MyConstructor.prototype // true
obj.foo()

So obj's prototype is MyConstructor.prototype. What's MyConstructor's prototype? Well, every function "inherits" from Function, so Object.getPrototypeOf(MyConstructor) === Function.prototype

As a side note, things get weird if you assign .prototype to something silly like:

function MyConstructor() {}
MyConstructor.prototype = "foo" // make objects inherit from... a string?
var obj = new MyConstructor()
Object.getPrototypeOf(obj) === MyConstructor.prototype // false! 

Upvotes: 10

lmiguelvargasf
lmiguelvargasf

Reputation: 69735

A prototype is the mechanism by which JavaScript objects inherit features from one another. [1]

Object.getPrototypeOf - this returns the prototype of a given object. [2]

The following code defines a constructor function, which can be used to build objects by putting the keyword new before its calling:

function Cat(name) {
    this.name = name;
}

Cat.prototype.meow = function (sound) {
    console.log(`Meow ${sound}`);
};

let cat = new Cat("Garfield");

Constructors, automatically get a property named prototype, which by default holds a plain, empty object that derives from Object.prototype. This property can be overridden, or you can also add properties as in the previous code.

It is crucial to understand the distinction between the way a prototype is associated with a constructor (through its prototype property) and the way objects have a prototype (which can be found with Object.getPrototypeOf).

The actual prototype of a constructor is Function.prototype since constructors are functions. Its prototype property holds the prototype used for instances created through it.

Hopefully at this point, the following code will leave clear what was stated before:

// This is a Cat object
> cat
Cat {name: "Garfield"}

> cat.prototype // notice that this object does not have the prototype property
undefined

// this is the constructor function
> Cat
ƒ Cat(name) {
    this.name = name;
}

// this is the prototype property of a constructor
> Cat.prototype
{meow: ƒ, constructor: ƒ}
meow: ƒ (sound)
constructor: ƒ Cat(name)
__proto__: Object

// this is true as previously stated
> Object.getPrototypeOf(Cat) == Function.prototype 
true

// this is true since the property prototype of a constructor is the
// prototype that objects created from it will have
> Object.getPrototypeOf(cat) == Cat.prototype
true

// Finally the prototype of the prototype property of a constructor
// is the same as Object.prototype
> Object.getPrototypeOf(Cat.prototype) == Object.prototype
true

Extra information

JS classes, introduced in ES6, are primarily syntactical sugar over JavaScript's existing prototype-based inheritance. The class syntax does not introduce a new object-oriented inheritance model to JavaScript.

So, the previous definition of Cat is equivalent to the following:

class Cat {
    constructor(name) {
        this.name = name;
    }

    meow(sound) {
        console.log(`Meow ${this.sound}`);
    }
}

Upvotes: 0

lukeaus
lukeaus

Reputation: 12255

TL;DR

function MyConstructor() {}

var obj = new MyConstructor()

Object.getPrototypeOf(obj) === obj.prototype // false
Object.getPrototypeOf(obj) === MyConstructor.prototype // true

MyConstructor.prototype // MyConstructor {}
obj.prototype // undefined

MyConstructor.prototype.constructor === MyConstructor  // true
Object.getPrototypeOf(MyConstructor) === Function.prototype // true

The confusing part about prototypes in javascript is that there are 2 different things that sound very similar.

When you create a new object, if the function or object used to create the new object has a .prototype method, then the object referenced by .prototype will become the new object's prototype newObj.__proto__.

Sounds complicated... let's break it down further.

A. The .prototype property

Example - Using a function as a constructor

When you use the new keyword on a function (i.e. you use the function as a constructor) then the function's .prototype becomes the new obj.__proto__.

Lets first make a function and checkout this .prototype property

function MyConstructor(){
}

console.log(MyConstructor.prototype)  // {}

Wait up... MyConstructor.prototype // {} - did something magically happen? Where did this empty object {} come from?

2 things here:

  1. Javascript automatically creates a .prototype object whenever you declare a function - automagically.

  2. This object is not empty. It actually has a property that points back to the function that created the object (the object's 'constructor'). Let's check it out:

console.log(MyConstructor.prototype.constructor); // [Function: MyConstructor]

MyConstructor.prototype.constructor === MyConstructor // true

So for functions, the .prototype property and it's associated object are created automatically.

Still confused? Lets add some methods in there to make it easier to see what's going on...

function MyConstructor(){
}

MyConstructor.prototype.func2 = function(){
};

console.log(MyConstructor);  // [Function: MyConstructor]
console.log(MyConstructor.prototype);  // MyConstructor { func2: [Function] }
MyConstructor.func2();  // TypeError: MyConstructor.func2 is not a function

Clearly we can see from the above code that MyConstructor and MyConstructor.prototype are 2 separate entities.

B. An object's prototype

An object's prototype (not .prototype - see A. above) is what javascript uses to lookup and resolve methods that aren't already in the object (more on this later).

Continuing on from above, when we create an object from a function or object that has a .prototype property, the newly created object will have it's object.__proto__ referencing this .prototype object.

An object's prototype can be accessed by

Object.getPrototypeOf(obj)

or the deprecated

obj.__proto__

Example - Using a function as a constructor

Lets make a new object using the function MyConstructor as a constructor.

function MyConstructor(){
}

var obj = new MyConstructor()

console.log(Object.getPrototypeOf(obj));  // {}

Here are the three relevant things:

  • MyConstructor (a function)
  • obj (an object that was created from MyConstructor)
  • obj.__proto__ --> MyConstructor.prototype

So obj.__proto__ is MyConstructor.prototype. Here is the proof:

MyConstructor.prototype === Object.getPrototypeOf(obj)  // true

Lets add a method to MyConstructor

function MyConstructor(){
  this.func1 = function(){
    console.log("this is func1");
  };
}

var obj = new MyConstructor();

obj.func1();  // this is func1

From the above you can see that you can call methods that were declared in the constructor. In fact, if we have a look, our declared method func1 is actually part of obj due to the way javascript creates objects.

console.log(obj); // MyConstructor { func1: [Function] }

We can also add methods that obj can use by adding the methods to the prototype. e.g.

MyConstructor.prototype.func2 = function(){
  console.log("this is func2");
};

obj.func2(); // this is func2

MyConstructor and MyConstructor.prototype methods will be available to all objects created using MyConstructor using this setup.


Useful References

Definitive Guide to Object-Oriented JavaScript

Understanding JavaScript: Inheritance and the prototype chain

A Plain English Guide to JavaScript Prototypes

Upvotes: 83

Willem van der Veen
Willem van der Veen

Reputation: 36600

Object.getPrototypeOf() vs .prototype

  • Prototype: Every object in javascript has a prototype. This is simply another object from which it 'inherits' properties and methods. This concept is called prototypal inheritance and is the only form of inheritance which exist in javascript. Constructs such as the class keyword in javascript is merely syntactic sugar built on top of this prototypal inheritance system.

    Every function has a prototype object property. When this function is then used as a constructor function with the new keyword the newly created object will inherit from this prototype object.

  • Object.getPrototypeOf(): Is a function which return a reference of this prototype object. We pass in an object as an argument and it will return the prototype object reference.

Example:

function Dog (name) {
  this.name = name;
}

// We can put properties on the prototype of the Dog constructor
Dog.prototype.bark = function () { console.log('woof'); };

let dog = new Dog('fluffie');
// Our newly created dog now has access to this bark method via the prototype chain
dog.bark();

// With the Object.getPrototypeOf method the Dog prototype object is returned
console.log(Object.getPrototypeOf(dog));

How is this useful?

Prototypal inheritance uses a prototype chain is a very powerful concept. It bascially works in the following manner:

  1. When you try to access a property, it will first look for the property on the object itself.
  2. When the property isn't found on the object itself it will look in the prototype of the object.
  3. When the property isn't found on the prototype it will climb up the prototype chain and look in the prototype of the objects prototype object. This will repeat untill there is no more higher chain in the prototype chain (i.e. no higher object to inherit from).

This enables the following advantages:

  1. We can use very convenient function which are located on native JS objects. For example, on Object.prototype and Array.prototype are many functions which provides us a lot of functionality. For example, we can use the functions of Array.prototype on any array via prototypal inheritance.
  2. We can define our own prototypes and extend form these prototypes. Extending from our own defined prototypes allows us to give an object functionality out of the box, when we are creating them.

Upvotes: 4

Marin Takanov
Marin Takanov

Reputation: 1138

function Foo() {
    // ...
}

var a = new Foo();

Object.getPrototypeOf( a ) === Foo.prototype; // true

When a is created by calling new Foo(), one of the things that happens is that a gets an internal [[Prototype]] link to the object that Foo.prototype is pointing at.

I suggest you to read "You don't know JavaScript" book series if you really want to learn in depth JavaScript.

Upvotes: 1

Related Questions