为什么instanceof在这里评估为真?

在这段代码中, f instanceof PipeWritable返回true(Node v8.4.0):

 const stream = require('stream'); const fs = require('fs'); class PipeWritable extends stream.Writable { constructor () { super(); } } const s = new PipeWritable(); const f = fs.createWriteStream('/tmp/test'); console.log(f instanceof PipeWritable); // true ... ??? 

对象s

  • Object.getPrototypeOf(s)PipeWritable {}
  • s.constructor[Function: PipeWritable]
  • PipeWritable.prototypePipeWritable {}

对象f

  • Object.getPrototypeOf(f)WriteStream { ... }
  • f.constructor[Function: WriteStream] ...
  • stream.WriteStream.prototypeWritable { ... }

原型链

 Object f Object s --------------------- -------------------- Writable PipeWritable Stream Writable EventEmitter Stream Object EventEmitter Object 

遵循instanceof的定义 :

instanceof运算符testing其原型链中的对象是否具有构造函数的原型属性。

我期望(f instanceof PipeWritable) === false ,因为PipeWritable不在f的原型链(上面的链通过调用Object.getPrototypeOf(...) )validation。
但是它回归true ,因此在我的分析中出了点问题。

什么是正确的答案?

这是由于_stream_writable.js中的Node.js源代码中的某个部分:

 var realHasInstance; if (typeof Symbol === 'function' && Symbol.hasInstance) { realHasInstance = Function.prototype[Symbol.hasInstance]; Object.defineProperty(Writable, Symbol.hasInstance, { value: function(object) { if (realHasInstance.call(this, object)) return true; return object && object._writableState instanceof WritableState; } }); } else { realHasInstance = function(object) { return object instanceof this; }; } 

通过语言规范 , instanceof运算符使用众所周知的符号@@hasInstance来检查对象O是否是构造函数C的一个实例:

12.9.4运行时语义:InstanceofOperator(O,C)

抽象操作InstanceofOperator(O,C)实现通用algorithm来确定对象O是否从构造函数C定义的inheritancepathinheritance。 这个抽象操作执行以下步骤:

  1. 如果Type ( C )不是Object,则抛出一个TypeErrorexception。
  2. instOfHandler成为GetMethod ( C ,@@ hasInstance)。
  3. ReturnIfAbrupt ( instOfHandler )。
  4. 如果instOfHandler不是未定义的 ,那么
    一个。 返回ToBoolean ( 调用 ( instOfHandlerC«O» ))。
  5. 如果IsCallable ( C )为false ,则引发TypeErrorexception。
  6. 返回OrdinaryHasInstance ( CO )。

现在让我来分解一下上面的代码:

 var realHasInstance; if (typeof Symbol === 'function' && Symbol.hasInstance) { … } else { … } 

上面的代码片段定义了realHasInstance ,检查Symbol是否定义以及是否存在着名的符号hasInstance 。 在你的情况,它的确如此,所以我们将忽略else分支。 下一个:

 realHasInstance = Function.prototype[Symbol.hasInstance]; 

这里, realHasInstance被赋值给Function.prototype[@@hasInstance]

19.2.3.6函数原型[@@ hasInstance](V)

当使用值V调用对象F的@@ hasInstance方法时,将执行以下步骤:

  1. F这个值。
  2. 返回普通有实例 ( FV )。

Function@@hasInstance方法只是调用OrdinaryHasInstance。 下一个:

 Object.defineProperty(Writable, Symbol.hasInstance, { value: function(object) { if (realHasInstance.call(this, object)) return true; return object && object._writableState instanceof WritableState; } }); 

这在Writable构造函数中定义了一个新属性,着名的符号hasInstance – 实质上实现了自己的hasInstance自定义版本。 hasInstance的值是一个函数,它接受一个参数,被instanceoftesting的对象,在这个例子中是f

下一行,if语句检查realHasInstance.call(this, object)是否真实。 前面提到, realHasInstance被分配给实际调用内部操作OrdinaryHasInstance(C,O)的 Function.prototype[@@hasInstance] 。 通过在原型链中查找构造函数,OrdinaryHasInstance操作只是检查O是否是您和MDN描述的C的一个实例。

在这种情况下,Writable f不是Writable( PipeWritable )的子类的实例,因此realHasInstance.call(this, object)为false。 既然这是错误的,那就到下一行了:

 return object && object._writableState instanceof WritableState; 

由于在这种情况下objectf是truthy,并且由于f是一个Writable,它具有一个WritableState实例的_writableState属性,因此f instanceof PipeWritabletrue


这个实施的原因是在评论中 :

 // Test _writableState for inheritance to account for Duplex streams, // whose prototype chain only points to Readable. 

由于Duplexstream在技术上是Writable,但是它们的原型链只指向Readable,因此需要额外的检查_writableState是否是WritableState一个实例,从而允许duplexInstance instanceof Writable为true。 这有一个副作用,你发现 – 一个可写是“一个孩子class的实例”。 这是一个错误,应该报告。

这实际上甚至在文档中报告:

注意: stream.Writable类原型地inheritance了stream.Readablestream.Writablestream.Writable ,但是instanceof将适用于两个基类,因为在Symbol.hasInstance上覆盖了stream.Writable

如下所示,从Writableinheritanceparasitcally有后果。


我在GitHub上提交了一个问题,看起来它会被修复。 正如Bergi提到的那样 ,添加一个检查来查看this === Writable ,当使用instanceof时确保只有Duplexstream是Writable的instanceof 。 有一个拉请求 。