Javascript:仍然对 instanceof 运算符感到困惑

Posted

技术标签:

【中文标题】Javascript:仍然对 instanceof 运算符感到困惑【英文标题】:Javascript: Still confused by the instanceof operator 【发布时间】:2015-08-04 10:22:07 【问题描述】:

article 定义 instanceof 如下:

instanceof 操作符测试一个对象是否在其原型中 链接构造函数的原型属性。

这是一个公平的解释,生活很美好,直到我从 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 的输出是“真”——因为构造函数 TextCell 是原型链的一部分。

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

如果看代码(倒数第 10 行),RTextCell 的原型被更新为一个新的 Object,其原型设置为 TextCell.prototype。

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

看下面的快照,对象“rt”的原型链中没有提到构造函数“RTextCell”。那么,按照我在文章开头提到的定义,输出不应该是假的吗?为什么会返回真值?

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

rt、RTextCell、TextCell 的快照按顺序见下文。

【问题讨论】:

这是一个结构很好的问题。您展示了您之前的所有调查,希望您得到很好的答案。 哦!我应该补充。我在 chrome 43.0.2357.65 和 firefox 33.1.1 上的上述快照中检查了这个原型链树。 【参考方案1】:
obj instanceof RTextCell

正在测试 RTextCell.prototype 是否存在于 obj 的原型链中。这是因为 obj 是使用新的 RTextCell 制作的。 RTextCell.prototype 的原型是 TextCell.prototype 的事实在这里是无关紧要的,似乎让你失望了。

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

没有去掉RTextCell.prototype,它仍然是一个对象,但它的原型恰好是TextCell.prototype。

【讨论】:

【参考方案2】:

准确的措辞很重要。您说 构造函数 在原型链中,但原始引用没有:

instanceof 操作符测试一个对象是否在其原型中 链接构造函数的原型属性

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

var p = rt.__proto__;
while(p)

  if(p == RTextCell.prototype)
    return true;
  p = p.__proto__;

return false;

因此,即使函数 RTextCell 没有在上面的对象树中直接引用,RTextCell.prototype 对象也是。

【讨论】:

或在单个表达式中,RTextCell.prototype.isPrototypeOf(rt) :-) @Dark Falcon:之前我对 instanceof 运算符和 isPrototypeOf() 感到困惑,当我在 MDN 网站上查看定义中的“构造函数”一词时,我完全误解了。你的回答让我停顿了一下,意识到我错过了什么。 “措辞”。现在它是有道理的。我也刚刚阅读了 [this].(***.com/questions/18343545/…) 以了解 instanceof 和 isPrototypeOf() 之间的区别。谢谢老兄。【参考方案3】:

您确实更改了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 right now 的值是否存在于对象的原型链中。对于RTextCell 实例,这始终是正确的,因为RTextCell 构造函数创建的实例总是在其原型链中获得RTextCell.prototype 的当前值,并且在开始构造实例后您永远不会更改RTextCell.prototype

【讨论】:

除了@Dark Falcon 的回答之外,您将原型设置为“somethingTotallyDifferent”的示例对我有所帮助。这是有道理的。谢谢。

以上是关于Javascript:仍然对 instanceof 运算符感到困惑的主要内容,如果未能解决你的问题,请参考以下文章

理解Javascript_07_理解instanceof实现原理

javascript typeof 和 instanceof 的区别和联系

JavaScript instanceof 运算符深入剖析

Javascript中的typeof和instanceof

关于JavaScript中的typeof与instanceof

JavaScript23_instanceof和hasOwn