Skip to content

Prototype VS __proto__

PhoenixAndMachine edited this page Apr 7, 2014 · 9 revisions

Prototype VS __proto__

Define a class

Suppose we define an object called Person such as

/* Class Person */

function Person(name, age) {
    this.name = name;
    this.age = age;
}

JS has done several things behind the curtain as follow,

  1. Person inherits from Function

    console.log("---------------Person inherits from Function----------------")
    console.log(Person.__proto__===Function.prototype); // true
    

    Person can call any methods and attributes defined in Function.prototype.

  2. Person is prepared its own prototype to be inherited

    console.log("---------------The prototype of Person----------------");
    console.log(Object.getOwnPropertyNames(Person.prototype));
    /*
    Output
        [ 'constructor' ]
    */
    

Define more methods

Person.prototype is for Person's instances to inherit from. We can define more methods or properties for Person's instances to inherit from, by

Person.prototype.getName = function() {
	return this.name;
};

Person.prototype.getAge = function() {
	return this.age;
};

There are two more methods defined in Person.prototype

console.log("---------------The prototype of Person----------------");
console.log(Object.getOwnPropertyNames(Person.prototype));
/*
Output
    [ 'getName', 'getAge', 'constructor' ]
*/

prototype VS __proto__

X.prototype is the model for X's instances to inherit from, which means, if X.prototype.method is defined, then instance_x.method is callable. X.__proto__ is an instance of X's superclass. Simply speaking, X.prototype is for other objects (child) to take, and X.__proto__ is taking from other objects (father). Let's take Person as X. When we call Person.constructor, Javascript will check Person's own properties first by

console.log(Object.getOwnPropertyNames(Person));
/*
    Output
	[ 'length', 'arguments', 'caller', 'prototype', 'name' ]
    But where those properties from ? 
    Since we define Person by Function, which means, in Function.prototype.constructor 
    it defines instances of Function will have these properties.
    Something like,
    Function.prototype.constructor = function(...) {
        this.length = ...
        this.arguments = ...
        ...
        ...
    }
*/

There is no constructor in this property list. Then JS will check Person.__proto__ which is Person's superclass.

console.log(Object.getOwnPropertyNames(Person.__proto__));
/*
	Output
		[ 'toString',
		  'call',
		  'caller',
		  'bind',
		  'apply',
		  'length',
		  'name',
		  'constructor',
		  'arguments' ]
*/

Yes, there is constructor, so when we call Person.constructor is actually calling Person.__proto__.constructor. Let's check our guess by

console.log(Person.constructor===Person.__proto__.constructor);
/*
	Output
		true
*/

And remember the fact that

console.log("---------------The superclass of Person is Function--------------------------");
console.log(Person.__proto__===Function.prototype);
/*
	Output
		true
*/

On the other hand Person.prototype.constructor will be called when an instance of Person is generated.

/* Instantiate Person */

var alice = new Person("Alice", 93);

console.log("---------------The prototype of alice----------------");
console.log(Object.getOwnPropertyNames(alice));
/*
	Output
		[ 'name', 'age' ]

*/
console.log(alice.constructor===alice.__proto__.constructor); // true

console.log(alice.__proto__===Person.prototype); // true

console.log(alice.constructor===Person.prototype.constructor); // true
console.log(alice.getName===Person.prototype.getName); //true
console.log(alice.getAge===Person.prototype.getAge); // true

console.log("--------------Alice--------------")
console.log("Name: " + alice.getName()); // Name: Alice
console.log("Age: " + alice.getAge()); // Age: 93

console.log(alice.prototype); // undefined

We can tell alice as an instance of Person which is generated by the definition of Person.prototype.constructor, and it inherits getName, getAge from Person, which are from Person.prototype.getName and Person.prototype.getAge.

There is the difference from the process that Person inherits from Function. When Person is defined by Function.prototype.constructor, from where Person.prototype is also generated. Therefore, Person.prototype is defined when function Person(...)=.... In Person.prototype.constructor, which actually is the function Person, we didn't define the down-ward inheritance for the instances of Person. So, when calling var alice=new Person(...), the alice.prototype is not defined.

In this part, we only illustrate and understand the inheritance in JS. We will explain more in Inheritance part. Here, I have to clear about prototype and __proto__ with a counter example here.

alice.prototype = {
	test: function() {
		throw new Error("It will not be called by alice, only some instances inheriting alice");
	}
}

try {
	alice.test();
}
catch(err) {
	console.log(err); // [TypeError: Object #<Person> has no method 'test']
}

alice.test() is called, JS will search it own properties without success, then will try to find it in alice.__proto__, which of course returns failure, and keep on alice.__proto__.__proto__, till alice.__proto__···__proto__ is null. The test is defined in alice.prototype, so it will only be called when there is an instance of alice calling test.

So simply speaking,

  • If you want X to have a father, deal with X.__proto__

  • If you want children of X to have some properties, deal with X.prototype

Space for Attributes and Methods

Let's continue our journey of JS.

/* Modify the class */

Person.prototype.getGreeting = function() {
	return "Hi " + this.getName() + "!";
}
console.log("---------------The prototype of Person------------------");
console.log(Object.getOwnPropertyNames(Person.prototype));

var bill = new Person("Bill", 27);

console.log("--------------Alice--------------")
console.log("Name: " + alice.getName());
console.log("Age: " + alice.getAge());
console.log("Greet: " + alice.getGreeting());
console.log("--------------Bill---------------")
console.log("Name: " + bill.getName());
console.log("Age: " + bill.getAge());
console.log("Greet: " + bill.getGreeting());

console.log("-------------------The __proto__ of instance is the class's prototype.------------------");
console.log(alice.__proto__ === bill.__proto__);
console.log(bill.__proto__ === Person.prototype);

console.log("-------------------The methods of instance used are defined in the class's prototype.------------------");
console.log(alice.getAge === bill.getAge);
console.log(alice.getAge === Person.prototype.getAge);
console.log(alice.__proto__.getAge === Person.prototype.getAge);

alice.displayGreeting = function() {
	console.log(this.getGreeting());
}

console.log("alice has own method called displayGreeting ? " + alice.hasOwnProperty("displayGreeting")); // true

alice.displayGreeting();

console.log("bill has own method called displayGreeting ? " + bill.hasOwnProperty("displayGreeting")); // false

bill.displayGreeting = function() {
	console.log(this.getGreeting());
}

console.log("------Those methods defined in instance are not equal, they are stored one copy in each instance.-----")
console.log(alice.displayGreeting === bill.displayGreeting); // false
  1. Dynamic

    In Java, C++, you have to define class first, then instantiate. After that, you are not able to change the definition of class, including methods, or attributes. In python, you have some approaches to add new methods to a class after defining one class. However, Javascript is born for this. We can tell from the example above, even after defining an instance alice of Person, we still can change the definition of Person, by adding one method getGreeting.

  2. Different instances inheriting from the same object share the same property space.

    In this example, bill and alice are instances which inherit from Person, their methods getAge, getName and getGreeting actually constructor are from the same space, only have one copy, referring to Person.prototype.

  3. Properties of object can be defined dynamically, they don't share the same copy.

    displayGreeting is defined in bill and alice, but they don't share the same copy. Object bill and alice keep displayGreeting in their own spaces.

Clone this wiki locally