为什么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.prototype
是PipeWritable {}
对象f
:
-
Object.getPrototypeOf(f)
是WriteStream { ... }
-
f.constructor
是[Function: WriteStream] ...
-
stream.WriteStream.prototype
是Writable { ... }
原型链 :
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。 这个抽象操作执行以下步骤:
- 如果Type ( C )不是Object,则抛出一个TypeErrorexception。
- 让instOfHandler成为GetMethod ( C ,@@ hasInstance)。
- ReturnIfAbrupt ( instOfHandler )。
- 如果instOfHandler不是未定义的 ,那么
一个。 返回ToBoolean ( 调用 ( instOfHandler , C , «O» ))。- 如果IsCallable ( C )为false ,则引发TypeErrorexception。
- 返回OrdinaryHasInstance ( C , O )。
现在让我来分解一下上面的代码:
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方法时,将执行以下步骤:
- 设F是这个值。
- 返回普通有实例 ( F , V )。
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
的值是一个函数,它接受一个参数,被instanceof
testing的对象,在这个例子中是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;
由于在这种情况下object
或f
是truthy,并且由于f
是一个Writable,它具有一个WritableState
实例的_writableState
属性,因此f instanceof PipeWritable
为true 。
这个实施的原因是在评论中 :
// 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.Readable
和stream.Writable
从stream.Writable
,但是instanceof
将适用于两个基类,因为在Symbol.hasInstance
上覆盖了stream.Writable
。
如下所示,从Writableinheritanceparasitcally有后果。
我在GitHub上提交了一个问题,看起来它会被修复。 正如Bergi提到的那样 ,添加一个检查来查看this === Writable
,当使用instanceof
时确保只有Duplexstream是Writable的instanceof
。 有一个拉请求 。