[英]Why does instanceof evaluate to true here?
In this snippet, the statement f instanceof PipeWritable
returns true (Node v8.4.0): 在此片段中,语句
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 ... ???
Object s
: 对象
s
:
Object.getPrototypeOf(s)
is PipeWritable {}
Object.getPrototypeOf(s)
是PipeWritable {}
s.constructor
is [Function: PipeWritable]
s.constructor
是[Function: PipeWritable]
PipeWritable.prototype
is PipeWritable {}
PipeWritable.prototype
是PipeWritable {}
Object f
: 对象
f
:
Object.getPrototypeOf(f)
is WriteStream { ... }
Object.getPrototypeOf(f)
是WriteStream { ... }
f.constructor
is [Function: WriteStream] ...
f.constructor
是[Function: WriteStream] ...
stream.WriteStream.prototype
is Writable { ... }
stream.WriteStream.prototype
是Writable { ... }
Prototype chains : 原型链 :
Object f Object s
--------------------- --------------------
Writable PipeWritable
Stream Writable
EventEmitter Stream
Object EventEmitter
Object
Following the definition of instanceof : 遵循instanceof的定义 :
The instanceof operator tests whether an object in its prototype chain has the prototype property of a constructor.
instanceof运算符测试其原型链中的对象是否具有构造函数的prototype属性。
I would expect that (f instanceof PipeWritable) === false
, because PipeWritable
is not in the prototype chain of f
(the chain above is verified by calls of Object.getPrototypeOf(...)
). 我希望
(f instanceof PipeWritable) === false
,因为PipeWritable
不在f
的原型链中(上面的链是通过调用Object.getPrototypeOf(...)
来验证的)。
But it returns true
, therefore something is wrong in my analysis. 但它返回
true
,因此我的分析中出现了问题。
What's the correct answer? 什么是正确的答案?
This is due to a certain part of code in the Node.js source, in _stream_writable.js
: 这是由于
_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;
};
}
By language specification , the instanceof
operator uses the well-known symbol @@hasInstance
to check if an object O is an instance of constructor C : 通过语言规范 ,
instanceof
运算符使用众所周知的符号@@hasInstance
来检查对象O是否是构造函数C的实例:
12.9.4 Runtime Semantics: InstanceofOperator(O, C)
12.9.4运行时语义:InstanceofOperator(O,C)
The abstract operation InstanceofOperator(O, C) implements the generic algorithm for determining if an object O inherits from the inheritance path defined by constructor C .
抽象操作InstanceofOperator(O,C)实现了通用算法,用于确定对象O是否继承自构造函数C定义的继承路径。 This abstract operation performs the following steps:
此抽象操作执行以下步骤:
- If Type ( C ) is not Object, throw a TypeError exception.
如果Type ( C )不是Object,则抛出TypeError异常。
- Let instOfHandler be GetMethod ( C ,@@hasInstance).
让instOfHandler成为GetMethod ( C ,@@ hasInstance)。
- ReturnIfAbrupt ( instOfHandler ).
ReturnIfAbrupt ( instOfHandler )。
- If instOfHandler is not undefined , then
如果未定义 instOfHandler ,那么
a.一个。 Return ToBoolean ( Call ( instOfHandler , C , «O» )).
返回ToBoolean ( 调用 ( instOfHandler , C , «O» ))。
- If IsCallable ( C ) is false , throw a TypeError exception.
如果IsCallable ( C )为false ,则抛出TypeError异常。
- Return OrdinaryHasInstance ( C , O ).
返回OrdinaryHasInstance ( C , O )。
Now let me break down the code above for you, section by section: 现在让我逐节为您分解上面的代码:
var realHasInstance;
if (typeof Symbol === 'function' && Symbol.hasInstance) {
…
} else {
…
}
The above snippet defines realHasInstance
, checks if Symbol
is defined and if the well-known symbol hasInstance
exists. 上面的代码段定义了
realHasInstance
,检查是否定义了Symbol
以及是否存在已知符号hasInstance
。 In your case, it does, so we'll ignore the else
branch. 在你的情况下,它确实如此,所以我们将忽略
else
分支。 Next: 下一个:
realHasInstance = Function.prototype[Symbol.hasInstance];
Here, realHasInstance
is assigned to Function.prototype[@@hasInstance]
: 这里,
realHasInstance
被赋值给Function.prototype[@@hasInstance]
:
19.2.3.6 Function.prototype[@@hasInstance] ( V )
19.2.3.6 Function.prototype [@@ hasInstance](V)
When the @@hasInstance method of an object F is called with value V , the following steps are taken:
当使用值V调用对象F的@@ hasInstance方法时,将执行以下步骤:
- Let F be the this value.
设F 为该值。
- Return OrdinaryHasInstance ( F , V ).
返回OrdinaryHasInstance ( F , V )。
The @@hasInstance
method of Function
just calls OrdinaryHasInstance. Function
的@@hasInstance
方法只调用OrdinaryHasInstance。 Next: 下一个:
Object.defineProperty(Writable, Symbol.hasInstance, {
value: function(object) {
if (realHasInstance.call(this, object))
return true;
return object && object._writableState instanceof WritableState;
}
});
This defines a new property on the Writable
constructor, the well-known symbol hasInstance
-- essentially implementing its own custom version of hasInstance
. 这在
Writable
构造函数上定义了一个新属性,众所周知的符号hasInstance
- 基本上实现了自己的hasInstance
自定义版本。 The value of hasInstance
is a function that takes one argument, the object that is being tested by instanceof
, in this case f
. hasInstance
的值是一个函数,它接受一个参数,即instanceof
正在测试的对象,在本例中为f
。
The next line, the if statement, checks if realHasInstance.call(this, object)
is truthy. 下一行if语句检查
realHasInstance.call(this, object)
是否真实。 Mentioned earlier, realHasInstance
is assigned to Function.prototype[@@hasInstance]
which is actually calling the internal operation OrdinaryHasInstance(C, O) . 前面提到过,
realHasInstance
被赋值给Function.prototype[@@hasInstance]
,它实际上调用了内部操作OrdinaryHasInstance(C,O) 。 The operation OrdinaryHasInstance just checks if O is an instance of C as you and MDN described, by looking for the constructor in the prototype chain. 通过查找原型链中的构造函数,OPERaryHasInstance操作只是检查O是否是您和MDN所描述的C的实例。
In this case, a Writable f
is not an instance of a subclass of Writable ( PipeWritable
) thus realHasInstance.call(this, object)
is false. 在这种情况下,Writable
f
不是Writable( PipeWritable
)的子类的实例,因此realHasInstance.call(this, object)
为false。 Since that is false, it goes to the next line: 由于这是错误的,它会转到下一行:
return object && object._writableState instanceof WritableState;
Since object
, or f
in this case, is truthy, and since f
is a Writable with a _writableState
property that is an instance of WritableState
, f instanceof PipeWritable
is true . 由于在这种情况下,
object
或f
是真实的,并且因为f
是具有_writableState
WritableState属性的WritableState
,它是WritableState
的实例,所以f instanceof PipeWritable
为true 。
The reason for this implementation is in the comments : 这个实现的原因在于评论 :
// Test _writableState for inheritance to account for Duplex streams,
// whose prototype chain only points to Readable.
Because Duplex streams are technically Writables, but their prototype chains only point to Readable, an extra check to see if _writableState
is an instance of WritableState
allows duplexInstance instanceof Writable
to be true. 因为Duplex流在技术上是可写的,但它们的原型链只指向Readable,额外检查以查看
_writableState
是否是WritableState
的实例,允许duplexInstance instanceof Writable
为true。 This has a side effect that you discovered -- a Writable being 'an instance of a child class'. 这有你发现的副作用 - Writable是'子类的一个实例'。 This is a bug and should be reported.
这是一个错误,应该报告。
This is actually even reported in the documentation : 这实际上甚至在文档中报告:
Note: The
stream.Duplex
class prototypically inherits fromstream.Readable
and parasitically fromstream.Writable
, butinstanceof
will work properly for both base classes due to overridingSymbol.hasInstance
onstream.Writable
.注:
stream.Duplex
从类prototypically继承stream.Readable
从寄生和stream.Writable
,但instanceof
将正常工作了,由于覆盖两基类Symbol.hasInstance
上stream.Writable
。
There are consequences to inheriting parasitcally from Writable as shown here. 如此处所示,从Writable继承parasitcally会产生后果。
I submitted an issue on GitHub and it looks like it'll be fixed. 我在GitHub上提交了一个问题,看起来它将被修复。 As Bergi mentioned , adding a check to see if
this === Writable
, making sure only Duplex streams were instances of Writable when using instanceof
. 正如Bergi所提到的那样 ,添加一个检查以查看是否
this === Writable
,确保在使用instanceof
时只有Duplex流是Writable的instanceof
。 There's a pull request . 有拉动请求 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.