-
Notifications
You must be signed in to change notification settings - Fork 3
Prototype VS __proto__
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,
-
Person inherits from Function
console.log("---------------Person inherits from Function----------------") console.log(Person.__proto__===Function.prototype); // truePerson can call any methods and attributes defined in
Function.prototype. -
Person is prepared its own prototype to be inherited
console.log("---------------The prototype of Person----------------"); console.log(Object.getOwnPropertyNames(Person.prototype)); /* Output [ 'constructor' ] */
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' ]
*/
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
Xto have a father, deal withX.__proto__ -
If you want children of
Xto have some properties, deal withX.prototype
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
-
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
aliceofPerson, we still can change the definition ofPerson, by adding one methodgetGreeting. -
Different instances inheriting from the same object share the same property space.
In this example,
billandaliceare instances which inherit fromPerson, their methodsgetAge,getNameandgetGreetingactuallyconstructorare from the same space, only have one copy, referring toPerson.prototype. -
Properties of object can be defined dynamically, they don't share the same copy.
displayGreetingis defined inbillandalice, but they don't share the same copy. ObjectbillandalicekeepdisplayGreetingin their own spaces.