Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript: Still confused by the instanceof operator

This article defines instanceof as below:

The instanceof operator tests whether an object has in its prototype chain the prototype property of a constructor.

That's a fair explanation and life was good until I came across this code from the book Eloquent Javascript:

function TextCell(text) {
  this.text = text.split("\n");
}

TextCell.prototype.minWidth = function() {
  return this.text.reduce(function(width, line) {
    return Math.max(width, line.length);
  }, 0);
}

TextCell.prototype.minHeight = function() {
  return this.text.length;
}

TextCell.prototype.draw = function(width, height) {
  var result = [];
  for (var i = 0; i < height; i++) {
    var line = this.text[i] || "";
    result.push(line + repeat(" ", width - line.length));
  }
  return result;
}

function RTextCell(text) {
  TextCell.call(this, text);
}

RTextCell.prototype = Object.create(TextCell.prototype);

RTextCell.prototype.draw = function(width, height) {
  var result = [];
  for (var i = 0; i < height; i++) {
    var line = this.text[i] || "";
    result.push(repeat(" ", width - line.length) + line);
  }
  return result;
};

Let's create an instance of RTextCell and execute the below c

var rt = new RTextCell("ABC");
console.log(rt instanceof RTextCell); // true
console.log(rt instanceof TextCell); // true

I understand why the output of the 2nd console.log is "true" - because constructor TextCell is part of the prototype chain.

However the 1st console.log confuses me.

If you look at the code (10th line from bottom), the prototype of RTextCell is updated to a new Object, whose prototype is set to TextCell.prototype.

RTextCell.prototype = Object.create(TextCell.prototype);.

Looking at the snapshots below, there is no mention of constructor "RTextCell" in the prototype chain of the object "rt". So, going by the definition which I mentioned at the beginning of my post, shouldn't the output be false? Why does it returns a true value?

I also read this but didn't help me to understand this specific problem.

See below for the snapshots of rt, RTextCell, TextCell in that order.

snapshot of "rt"snapshot of RTextCellsnapshot of TextCell

like image 931
Harish Avatar asked May 22 '15 11:05

Harish


2 Answers

You do change RTextCell.prototype, but you change it before you construct any RTextCell instances. Consider this massively different example, where RTextCell.prototype is modified after an instance is created with the original prototype:

var rt = new RTextCell();
RTextCell.prototype = somethingTotallyDifferent;
rt instanceof RTextCell; // false!

When rt is created, it is true that rt.__proto__ === RTextCell.prototype. Once RTextCell.prototype changes, that stops being true.

You're not testing if rt has the original prototype property from RTextCell in its prototype chain. Rather, you're testing if the value of RTextCell.prototype right now exists in the object's prototype chain. That will always be true for RTextCell instances, because instances created by the RTextCell constructor always get the current value of RTextCell.prototype in their prototype chain, and you never change RTextCell.prototype after you start constructing instances.

like image 180
apsillers Avatar answered Sep 28 '22 04:09

apsillers


The exact wording is important. You talk about the constructor being in the prototype chain, but the original quote doesn't:

The instanceof operator tests whether an object has in its prototype chain the prototype property of a constructor.

So the expression rt instanceof RTextCell actually is testing something like this (keeping in mind that __proto__ is not standard):

var p = rt.__proto__;
while(p)
{
  if(p == RTextCell.prototype)
    return true;
  p = p.__proto__;
}
return false;

So even though the function RTextCell is not referenced directly in the object trees above, the RTextCell.prototype object is.

like image 45
Dark Falcon Avatar answered Sep 28 '22 04:09

Dark Falcon