Javascript: 还是被 instanceof 运算符搞糊涂了

Javascript: Still confused by the instanceof operator

这个 article 定义 instanceof 如下:

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

这是一个公平的解释,生活很美好,直到我从书中看到这段代码 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;
};

让我们创建一个 RTextCell 实例并执行下面的 c

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

我明白为什么第二个 console.log 的输出是 "true" - 因为构造函数 TextCell 是原型链的一部分。

但是第一个 console.log 让我很困惑。

如果您查看代码(从底部开始第 10 行),RTextCell 的原型将更新为一个新的对象,其原型设置为 TextCell.prototype。

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

查看下面的快照,在对象 "rt" 的原型链中没有提到构造函数 "RTextCell"。那么,按照我在 post 开头提到的定义,输出不应该是假的吗?为什么它 returns 是一个真值?

我也读过 this 但没有帮助我理解这个具体问题。

按顺序查看 rt、RTextCell、TextCell 的快照。

obj instanceof RTextCell

正在测试obj的原型链中是否存在RTextCell.prototype。这是因为 obj 是使用新的 RTextCell 制作的。 RTextCell.prototype 具有原型 TextCell.prototype 的事实与这里的重点无关,似乎让您感到困惑。

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

没有去掉RTextCell.prototype,它还是一个对象,只是它的原型恰好是TextCell.prototype.

准确的措辞很重要。你说 constructor 在原型链中,但原来的引用没有:

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

所以表达式 rt instanceof RTextCell 实际上是在测试这样的东西(记住 __proto__ 不是标准的):

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

所以即使在上面的对象树中没有直接引用函数 RTextCellRTextCell.prototype 对象是。

您确实更改了 RTextCell.prototype,但是您在构建任何 RTextCell 实例之前更改了它。考虑这个截然不同的示例,其中 RTextCell.prototype 在使用原始原型创建实例后被修改:

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

创建rt时,rt.__proto__ === RTextCell.prototype为真。一旦 RTextCell.prototype 改变,那就不再是真的了。

您不是在测试 rt 是否在其原型链中具有来自 RTextCell 原始 prototype 属性。相反,您正在测试 RTextCell.prototype 现在 的值是否存在于对象的原型链中。 RTextCell 实例总是如此,因为由 RTextCell 构造函数创建的实例总是在它们的原型链中获得 RTextCell.prototype 的当前值,并且你永远不会在之后更改 RTextCell.prototype你开始构建实例。