gpt4 book ai didi

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

转载 作者:IT老高 更新时间:2023-10-28 22:12:10 28 4
gpt4 key购买 nike

在此代码段中,语句 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 { ... }

原型(prototype)链:

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

按照 definition of instanceof :

The instanceof operator tests whether an object in its prototype chain has the prototype property of a constructor.

我希望 (f instanceof PipeWritable) === false,因为 PipeWritable 不在 f 的原型(prototype)链中(上面的链通过调用 Object.getPrototypeOf(...)) 进行验证。
但它返回 true,因此我的分析有问题。

正确答案是什么?

最佳答案

这是由于 Node.js 源代码中的某个部分代码,在 _stream_writable.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;
};
}

作者 language specification , instanceof运算符使用众所周知的符号 @@hasInstance检查对象 O 是否是构造函数 C 的实例:

12.9.4 Runtime Semantics: 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. This abstract operation performs the following steps:

  1. If Type(C) is not Object, throw a TypeError exception.
  2. Let instOfHandler be GetMethod(C,@@hasInstance).
  3. ReturnIfAbrupt(instOfHandler).
  4. If instOfHandler is not undefined, then
    a. Return ToBoolean(Call(instOfHandler, C, «O»)).
  5. If IsCallable(C) is false, throw a TypeError exception.
  6. Return 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 Function.prototype[@@hasInstance] ( V )

When the @@hasInstance method of an object F is called with value V, the following steps are taken:

  1. Let F be the this value.
  2. Return OrdinaryHasInstance(F, V).

@@hasInstance Function的方法|只需调用 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 正在测试的对象, 在这种情况下 f .

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

在这种情况下,可写 f不是 Writable ( PipeWritable ) 子类的实例,因此 realHasInstance.call(this, object)是假的。既然是假的,就转到下一行:

return object && object._writableState instanceof WritableState;

自从 object , 或 f在这种情况下,是真实的,并且因为 f是一个可写的 _writableState作为 WritableState 实例的属性, f instanceof PipeWritable真实


这个实现的原因是在 comments :

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

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

这实际上甚至在 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.

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


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

关于javascript - 为什么 instanceof 在这里评估为真?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45772705/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com