首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >为什么instanceof在这里的计算结果为true?

为什么instanceof在这里的计算结果为true?
EN

Stack Overflow用户
提问于 2017-08-19 22:28:10
回答 1查看 677关注 0票数 19

在这段代码中,语句f instanceof PipeWritable返回true (节点v8.4.0):

代码语言:javascript
运行
复制
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

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

PipeWritable {}

f**:**的Object

  • Object.getPrototypeOf(f) is WriteStream { ... }
  • stream.WriteStream.prototype is Writable { ... }

is WriteStream { ... } [Function: WriteStream] ... isWritable { ... }isWriteStream { ... }

  • stream.WriteStream.prototypeisWritable { ... }

isWriteStream { ... }[Function: WriteStream] ...is[Function: WriteStream] ...

原型链

代码语言:javascript
运行
复制
Object f                    Object s
---------------------       --------------------
  Writable                    PipeWritable
    Stream                      Writable
      EventEmitter                Stream
        Object                      EventEmitter
                                      Object

遵循definition of

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

我希望是(f instanceof PipeWritable) === false,因为PipeWritable不在f的原型链中(上面的链是通过调用Object.getPrototypeOf(...)来验证的)。

但是它返回true,因此在我的分析中有些地方是错误的。

正确答案是什么?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-08-19 23:08:52

这是由于_stream_writable.js中Node.js源代码中的某一部分代码造成的

代码语言:javascript
运行
复制
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;
  };
}

通过language specificationinstanceof操作符使用众所周知的符号@@hasInstance来检查对象O是否是构造函数C的实例

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

抽象操作InstanceofOperator(O,C)实现了用于确定对象O是否从构造函数C定义的继承路径继承的通用算法。该抽象操作执行以下步骤:

如果Object(C)不是Object,则抛出一个未定义的对象(C,@@hasInstance).

  • ReturnIfAbrupt(instOfHandler).

  • If instOfHandler is Type TypeError instOfHandler be GetMethod,C,TypeErrorinstOfHandler beGetMethod,则

a. exception.

  • Return ToBoolean(Call(instOfHandler,C,«O»))。

  • 如果IsCallable(C)是false,则抛出TypeError OrdinaryHasInstance(C,O)。

现在让我一节一节地为你分解上面的代码:

代码语言:javascript
运行
复制
var realHasInstance;
if (typeof Symbol === 'function' && Symbol.hasInstance) {
  …
} else {
  …
}

上面的代码片段定义了realHasInstance,检查是否定义了Symbol,以及是否存在熟知的符号hasInstance。在您的例子中,它是这样的,所以我们将忽略else分支。接下来:

代码语言:javascript
运行
复制
realHasInstance = Function.prototype[Symbol.hasInstance];

在这里,realHasInstance被分配给Function.prototype[@@hasInstance]

19.2.3.6 Function.prototype@@hasInstance

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

设F为value.

  • Return

  • (F,V) OrdinaryHasInstance(F,V)。

Function@@hasInstance方法只调用OrdinaryHasInstance。接下来:

代码语言:javascript
运行
复制
Object.defineProperty(Writable, Symbol.hasInstance, {
  value: function(object) {
    if (realHasInstance.call(this, object))
      return true;

    return object && object._writableState instanceof WritableState;
  }
});

这在Writable构造函数上定义了一个新属性,即众所周知的symbol hasInstance --本质上实现了它自己的定制版本的hasInstancehasInstance的值是一个带有一个参数的函数,该参数是由instanceof测试的对象,在本例中为f

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

在这种情况下,可写的f不是可写(PipeWritable)的子类的实例,因此realHasInstance.call(this, object)为false。因为这是假的,所以它转到下一行:

代码语言:javascript
运行
复制
return object && object._writableState instanceof WritableState;

由于object (本例中的f )是真的,并且f是一个具有_writableState属性的Writable,该属性是WritableState的一个实例,因此f instanceof PipeWritabletrue

此实现的原因是在comments

代码语言:javascript
运行
复制
// Test _writableState for inheritance to account for Duplex streams,
// whose prototype chain only points to Readable.

因为双工数据流在技术上是可写的,但它们的原型链只指向可读,所以额外检查_writableState是否是WritableState的实例允许duplexInstance instanceof Writable为真。这有一个你发现的副作用--一个可写的是“一个子类的实例”。这是一个错误,应该报告。

实际上,这甚至在documentation中也有报道

注意:stream.Duplex类典型地继承自stream.Readable并寄生于stream.Writable,但是instanceof将为这两个基类正常工作,因为它覆盖了stream.Writable上的Symbol.hasInstance

如下所示,从Writable中寄生继承会产生一些后果。

我提交了一个issue on GitHub,它看起来会被修复。作为Bergi mentioned,添加检查以查看this === Writable,确保在使用instanceof时只有双工流是可写的实例。有一个pull request

票数 16
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45772705

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档