If I have a function (constructor) with a defined prototype, let’s say:
function Student(name) {
this.name = name;
}
Student.prototype.sayHello = function() {
console.log(`Hello, my name is ${this.name}`);
}
And I instantiate and object using that function (from what I know it should set newly created object’s property _prototype
to the above prototype object in the moment of creation and set constructor
property to Student
function).
const s1 = new Student('John');
s1.sayHello();
I am sure calling sayHello()
would work, however, what if I change the Student
‘s prototype like this?
Student.prototype.sayGoodbye = function() {
console.log(`Goodbye, my name is ${this.name}`);
}
Should s1
be able to call sayGoodbye
?
I’ve read that the _prototype
should’t change, so in that logic it should throw an error, however, trying it in Chrome’s console panel and node.js project it worked and s1 could invoke sayGoodbye
. Can someone help me understand this?
2
Answers
I am not sure what
_prototype
means. Do you mean.__proto__
? Or.prototype
? Those two are different things (and.__proto__
is deprecated, in favour ofObject.getPrototypeOf
andObject.setPrototypeOf
, and similarReflect
methods).There are a couple of different meanings of "change the prototype".
Replace the
[[Prototype]]
, like withObject.setPrototypeOf
. Besides the possible implications on the readability, there is also the performance factor, summarised in this warning from the MDN:Modify the
[[Prototype]]
of built-in or library objects, by replacing or adding properties. This is discouraged mainly because it is not future-proof. For example,Array.prototype.find
is a pretty useful function, which did not exist in, say, 2012. If someone added it to the array prototype, it would have been useful then, but when it got implemented a few years later, anyone who definedArray.prototype.find
on their own would have been replacing a more efficient code with the less efficient one.You could guard against it and only replace the function if it wasn’t already defined, but then incompatibilities could arise. The now-standard
.find
will returnundefined
when it can’t find a matching element. If your polyfill is returningnull
, but you leave alone the built-in.find
in case it exists, and your code behaves differently onnull
andundefined
, you have a problem.Modify the
[[Prototype]]
of your own objects, by defining or replacing properties on the prototype. It seems this is the meaning you are using in your question. Shrug. It’s your code.Yes, any changes to the prototype properties will be reflected in the prototyped objects, unless they are overshadowed by the properties on the prototyped objects. As deceze explains in their comment, the prototype properties are not copied onto the prototyped object at creation time. Instead, at lookup time, the property is checked on the prototyped object, then on the prototype, then on the prototype’s prototype, going up the prototype chain until it is found (or, if no such property is found until the end of the chain is reached at the root object, returning
undefined
). Thus, if you add a property to a prototype, it makes no difference when the property is added, whether before or after the creation of a prototyped object, as long as it is before the property is accessed.As an answer to your question "Should s1 be able to call sayGoodbye?"
Yes, it should.
To be clear why. You have to understand the [[prototype]] chain. Any constructor function like your function:
is an object which has a .prototype property. Any instance of that constructor function
var student = new Student("name");
will have a property__proto__
which will point toStudent.prototype
which in turn has a__proto__
property which point to Object.prototype (the end of the [[prototype]] chain).now, when you add a property to any prototype node in that chain -let’s say
sayGoodbye
– it will be seen by the instancestudent
, because when you call it like this:student.sayGoodbye()
, JS engine will look for it in student object if it did not find it, then the engine will look for it in what everstudent.__proto__
refer to, which in our caseStudent.prototype
if it also did not find it there it will continue lokking in the [[prototype]] chain until it find it or reach the end of the chain which isObject.prototype
.