skip to Main Content

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


  1. I am not sure what _prototype means. Do you mean .__proto__? Or .prototype? Those two are different things (and .__proto__ is deprecated, in favour of Object.getPrototypeOf and Object.setPrototypeOf, and similar Reflect methods).

    There are a couple of different meanings of "change the prototype".

    1. Replace the [[Prototype]], like with Object.setPrototypeOf. Besides the possible implications on the readability, there is also the performance factor, summarised in this warning from the MDN:

      Warning: Changing the [[Prototype]] of an object is, by the nature of how modern JavaScript engines optimize property accesses, currently a very slow operation in every browser and JavaScript engine. In addition, the effects of altering inheritance are subtle and far-flung, and are not limited to the time spent in the Object.setPrototypeOf(...) statement, but may extend to any code that has access to any object whose [[Prototype]] has been altered. You can read more in JavaScript engine fundamentals: optimizing prototypes.

      Because this feature is a part of the language, it is still the burden on engine developers to implement that feature performantly (ideally). Until engine developers address this issue, if you are concerned about performance, you should avoid setting the [[Prototype]] of an object. Instead, create a new object with the desired [[Prototype]] using Object.create().

    1. 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 defined Array.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 return undefined when it can’t find a matching element. If your polyfill is returning null, but you leave alone the built-in .find in case it exists, and your code behaves differently on null and undefined, you have a problem.

    2. 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.

    Login or Signup to reply.
  2. 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:

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

    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 to Student.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 instance student, 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 ever student.__proto__ refer to, which in our case Student.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 is Object.prototype.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search