Search code examples
javascriptprototype

Redefining prototype vs appending to prototype in Javascript


From here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures

Performance considerations

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype = {
  getName: function() {
    return this.name;
  },
  getMessage: function() {
    return this.message;
  }
};

However, redefining the prototype is not recommended. The following example instead appends to the existing prototype:

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype.getName = function() {
  return this.name;
};
MyObject.prototype.getMessage = function() {
  return this.message;
};

Above quotes and codes are from the given link.
I want to understand how does the first code suggest that we are redefining the prototype and how does the second code suggest that we are appending the prototype?

What represents redefining and what represents appending in the above pieces of code?


Solution

  • When you redefine the prototype object you are creating a whole new object, instead of preserving the existing prototype with its properties, this is undesirable.

    If we redefine the prototype object, it will break the inheritance hierarchy. Particularly instanceof and Object.prototype.isPrototypeOf won't work as expected.

    Let's take an example and show a simple inheritance hierarchy with ChildClassOne and ChildClassTwo inheriting from ParentClass.

    In the first case the inheritance is broken as the prototype is overriden with a completely new object which erases every information which was present already, instead of appending to the prototype received from the Object.create call.

    In the second case it works as expected as the prototype is not replaced:

    function ParentClass(param) {
      this.param = param;
    }
    ParentClass.prototype.getParam = function() {
      return this.param;
    }
    
    function ChildClassOne(param, name, message) {
      ParentClass.call(param);
      this.name = name.toString();
      this.message = message.toString();
    }
    //Inheriting through prototype
    ChildClassOne.prototype = Object.create(ParentClass.prototype);
    ChildClassOne.constructor = ChildClassOne;
    
    //Prototype completely overriden, destroying the hierarchy
    ChildClassOne.prototype = {
      getName: function() {
        return this.name;
      },
      getMessage: function() {
        return this.message;
      }
    };
    
    //the instanceof and isPrototypeOf is broken
    const child1 = new ChildClassOne("bazz", "foo", "bar");
    console.log(child1 instanceof ParentClass);
    console.log(ParentClass.prototype.isPrototypeOf(child1));
    console.log(ChildClassOne.prototype instanceof ParentClass)
    
    function ChildClassTwo(param, name, message) {
      ParentClass.call(param);
      this.name = name.toString();
      this.message = message.toString();
    }
    
    //Inheriting through prototype
    ChildClassTwo.prototype = Object.create(ParentClass.prototype);
    ChildClassTwo.constructor = ChildClassTwo;
    
    //Preserving the heirarchy information, by appending to existing prototype
    ChildClassTwo.prototype.getName = function() {
      return this.name;
    };
    ChildClassTwo.prototype.getMessage = function() {
      return this.message;
    }
    
    //Works as expected
    const child2 = new ChildClassTwo("bazz", "foo", "bar");
    console.log(child2 instanceof ParentClass);
    console.log(ParentClass.prototype.isPrototypeOf(child2));
    console.log(ChildClassTwo.prototype instanceof ParentClass)