简体   繁体   English

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

[英]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.prototypePipeWritable {}

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.prototypeWritable { ... }

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: 此抽象操作执行以下步骤:

  1. If Type ( C ) is not Object, throw a TypeError exception. 如果TypeC )不是Object,则抛出TypeError异常。
  2. Let instOfHandler be GetMethod ( C ,@@hasInstance). instOfHandler成为GetMethodC ,@@ hasInstance)。
  3. ReturnIfAbrupt ( instOfHandler ). ReturnIfAbruptinstOfHandler )。
  4. If instOfHandler is not undefined , then 如果未定义 instOfHandler ,那么
    a. 一个。 Return ToBoolean ( Call ( instOfHandler , C , «O» )). 返回ToBoolean调用instOfHandlerC«O» ))。
  5. If IsCallable ( C ) is false , throw a TypeError exception. 如果IsCallableC )为false ,则抛出TypeError异常。
  6. Return OrdinaryHasInstance ( C , O ). 返回OrdinaryHasInstanceCO )。

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方法时,将执行以下步骤:

  1. Let F be the this value. F 为该值。
  2. Return OrdinaryHasInstance ( F , V ). 返回OrdinaryHasInstanceFV )。

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 . 由于在这种情况下, objectf是真实的,并且因为f是具有_writableState WritableState属性的WritableState ,它是WritableState的实例,所以f instanceof PipeWritabletrue


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 from stream.Readable and parasitically from stream.Writable , but instanceof will work properly for both base classes due to overriding Symbol.hasInstance on stream.Writable . 注: stream.Duplex从类prototypically继承stream.Readable从寄生和stream.Writable ,但instanceof将正常工作了,由于覆盖两基类Symbol.hasInstancestream.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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM