I was learning inheritance and Prototype chaining. I was stuck after seeing this behaviour of resetting the constructor function's prototype inside the function itself. On instantiating
- Case
const Person = function (name,age) {
this.name = name;
this.age = age;
}
Person.prototype.calcAge = function () {
console.log(`My age is ${2022-this.age}`);
}
const Student = function (name,age,course) {
Person.call(this,name,age);
this.course = course;
Student.prototype = null; // This should set Student's Prototype to null and
// thus should not let instance to access intro property.
}
Student.prototype.intro = function () {
console.log(`I am ${this.name} and my course is ${this.course}`)
};
const mike = new Student("mike",2000,"cse");
mike.intro();
In the code above, the output is I am mike and my course is cse
but while instantiating I did set Student.prototype = null;
so how is mike instance still accessing intro method.
CodePudding user response:
At the moment you call new
, the prototype object is taken from Student.prototype
and used to create this
. So at that moment the proto of this
is the object that Student.prototype
references.
It is that proto reference that is later used to find the definition of intro
. It is found via Object.getPrototypeOf(mike)
, not via Student.prototype
. The latter only serves for future constructions.
By setting Student.prototype
to null
, you only determine what will happen with the next use of new Student
. But for the instance that was already created, it comes too late. Object.getPrototypeOf(this)
(when in the constructor's execution), will still refer to the original prototype object.
Here some extra console.log
to illustrate that point:
const Person = function (name,age) {
this.name = name;
this.age = age;
}
Person.prototype.calcAge = function () {
console.log(`My age is ${2022-this.age}`);
}
const Student = function (name,age,course) {
Person.call(this,name,age);
this.course = course;
// true:
console.log(Student.prototype === Object.getPrototypeOf(this));
Student.prototype = null;
// false:
console.log(Student.prototype === Object.getPrototypeOf(this));
}
Student.prototype.intro = function () {
console.log(`I am ${this.name} and my course is ${this.course}`)
};
const mike = new Student("mike",2000,"cse");
// true:
console.log(Object.getPrototypeOf(mike).hasOwnProperty("intro"));
mike.intro();
CodePudding user response:
According to MDN, the new
operator does 4 things when used to call a function, of which the second is:
Points newInstance's [[Prototype]] to the constructor function's prototype property.
And it adds the following note:
Note: Properties/objects added to the constructor function's prototype property are therefore accessible to all instances created from the constructor function.
This is presumably known to you, and why you are expecting setting Student.prototype
to null
to stop any method calls on a Student
instance from working.
But there is a big difference between:
- "adding (functions) to the constructor function's prototype property" (which is what the above quote talks about, and what you do in setting
Student.prototype.intro
to a function), and - completely replacing the function's prototype property with a new value (in your case
null
)
When your example code runs, the above step involved in calling the new
operator goes through as normal. Note that Student.prototype
exists as soon as you declare the Student
function - and it's (to all intents and purposes, anyway), an empty object. So the new Student instance - mike
in your case - will be linked via the prototype chain to this object Student.prototype
. Which contains an intro
method because you added one explicitly (and you did so before instantiating the object).
When you set Student.prototype
to null
, later in the constructor, that doesn't change at all what mike
's prototype is (in the sense of the object that JS will look up property/method accesses on if you access a non-existent property, such as intro
, on mike
). That's still the same object it was before - an essentially empty one, with an intro
method added. That object still exists, even though it's no longer accessible as Student.prototype
. And it's still prototype-linked to mike
, because that link already happened when you instantiated mike
.
Of course, since Student.prototype
is null
after that first instantiation, things will go very wrong on the second one:
const Person = function (name,age) {
this.name = name;
this.age = age;
}
Person.prototype.calcAge = function () {
console.log(`My age is ${2022-this.age}`);
}
const Student = function (name,age,course) {
Person.call(this,name,age);
this.course = course;
Student.prototype = null; // This should set Student's Prototype to null and
// thus should not let instance to access intro property.
}
Student.prototype.intro = function () {
console.log(`I am ${this.name} and my course is ${this.course}`)
};
const mike = new Student("mike",2000,"cse");
const bob = new Student("bob", 1000, "math");
mike.intro();
bob.intro();
but (as still seen above), the first invocation is absolute fine.
If you absolutely need to remove one or more methods on the first instantiation (although why you want to do this, I cannot guess), then you need to mutate the Student.prototype
reference rather than overwriting it. So replace
Student.prototype = null;
with
Student.prototype.intro = null;
(or similar)