Search code examples
javascriptinheritanceecmascript-6prototypeprototypal-inheritance

Difference between inheriting using .call() and prototype


I was recently learning javaScript and stumbled across this

  function Polygon() {
  this.dimensions = "2d";
  this.print = function () {
    console.log(" 2d dimensions are easy to work with!");
  }
}
function Quad() {
  Polygon.call(this);
  this.sides = 4;
}
var quad = new Quad();
quad.print();

function Polygon1() {}
Polygon1.prototype.dimensions = "2d";
Polygon1.prototype.print = console.log("2d dimensions are not difficult to work with!");

function Quad1() {
  this.sides = 4;
}
Quad1.prototype = Object.create(Polygon1.prototype);
Quad1.prototype.constructor = Quad1;

var quad1 = new Quad1();
quad1.print();

in both the cases i'm able to call the print function, so what is the difference between these two ways of inheriting, or have i done something wrong here?


Solution

  • In JavaScript, it's about the object ownership. Object.call isn't a permanent ownership change, it essentially "calls" another function, with the ownership set as the calling function for that call, therefore passing it's own properties, defined by this to the "owned" function.

    eg.

    function Polygon() {
      this.sides = 2;
      this.dimensions = "2d";
      var _this = this;
      this.print = function () {
        console.log("%s dimensions are easy to work with!, sides: ", _this.dimensions, _this.sides);
      }
    }
    function Quad() {
      Polygon.call(this);
      this.sides = 4;
    }
    new Quad().print();
    // Outputs: 2d dimensions are easy to work with!, sides:  4
    

    By calling Polygon.call, with this as the property, it replaces Polygon's this reference with the this from Quad, but only in the scope of the call, essentially copying the changes made to this in Polygon, to the Quad this.

    See the following example:

    function Polygon(sides) {
      this.sides = sides;
      this.dimensions = "2d";
      var _this = this;
      this.print = function () {
        console.log("%s dimensions are easy to work with!, sides: ", _this.dimensions, _this.sides);
      }
    }
    function Quad() {
      Polygon.call(this);
      this.sides = 4;
    }
    const poly = new Polygon(2);
    const quad = new Quad();
    quad.print(); // 2d dimensions are easy to work with!, sides:  4
    poly.print(); // 2d dimensions are easy to work with!, sides:  2
    poly.print !== quad.print; // should return true
    

    Using Object.prototype is different because you're directly modifying the object, whereas with Call, you're calling a function with implied ownership, on that call alone. Where the two examples differ, is that the prototype example creates an instance of Polygon directly on the Quad object, while Call never explicitly does this.

    Both directions have their strengths and weaknesses, different methods to test, etc.