检查对象是否是类的“直接实例”

12

我有两个类:

class Bar extends Foo { // Foo isn't relevant
  constructor(value) {
    if (!(value instanceof Foo)) throw "InvalidArgumentException: (...)";
    super();
    this.value = value;
  }
}

class Baz extends Bar {
  constructor(value) {
    super(value);
  }
}
Bar构造函数检查value是否是Foo的实例,如果不是则抛出错误。至少,这是我想让它做的。如果你传递一个BarBaz作为值,if语句也会返回true。目标是只允许Foo通过。
我已经找到这个答案,但那并没有真正回答我的问题。

你链接的另一个问题中的这个答案似乎可行。只有通过 new Foo() 创建的Foo对象调用foo.constructor.name时才会返回“ Foo”。 - SystemGlitch
1
你认为你需要这个吗?看起来你正在滥用继承,因为你的目标是打破Liskov替换原则。不要那样做。每个BarBaz实例都应该在你想要一个Foo实例的地方有效。 - Bergi
7个回答

11

检查构造函数:

if (!value || value.constructor !== Foo)
  throw 'InvalidArgumentException: (...)';

或者对象的原型(这更类似于instanceof的工作方式):

if (!value || Object.getPrototypeOf(value) !== Foo.prototype)
  throw 'InvalidArgumentException: (...)';

1
这并没有回答核心问题。它没有解决继承问题。 - Joshua Manns

2
你可以使用Object.getPrototypeOf(yourObj)Foo.prototype之间的比较来确定yourObj是否确切地是Foo的实例。只需要继续调用每个级别的Object.get prototypeOf,就可以向上移动链。
例如:

class Foo {}

class Bar extends Foo {}
class Baz extends Bar {}

const foo = new Foo();
const bar = new Bar();
const baz = new Baz();

// For this function:
// - level 0 is self
// - level 1 is parent
// - level 2 is grandparent
// and so on.
function getPrototypeAt(level, obj) {
    let proto = Object.getPrototypeOf(obj);
    while (level--) proto = Object.getPrototypeOf(proto);
    return proto;
}

console.log("bar is a foo:", bar instanceof Foo);
console.log("baz is a foo:", baz instanceof Foo);
console.log("foo is exactly a foo:", getPrototypeAt(0, foo) === Foo.prototype);
console.log("bar is exactly a foo:", getPrototypeAt(0, bar) === Foo.prototype);
console.log("bar is direct child of foo:", getPrototypeAt(1, bar) === Foo.prototype);
console.log("baz is direct child of foo:", getPrototypeAt(1, baz) === Foo.prototype);
console.log("baz is direct child of bar:", getPrototypeAt(1, baz) === Bar.prototype);
console.log("baz is grandchild of foo:", getPrototypeAt(2, baz) === Foo.prototype);


1
你应该测试value的内部[[Prototype]]是否正好是Foo.prototype。你可以使用Object.getPrototypeOf来获取内部[[Prototype]]
if ( Object.getPrototypeOf( value ) !== Foo.prototype )
   throw "InvalidArgumentException: (...)";

0
如果你知道所有的类,你可以使用
if(!(value instanceof Foo && !(value instanceof Bar) && !(value instanceof Baz)))

@Sacha,请查看我的答案,以进行对其他类的通用检查。 - Joshua Manns

0

这是辅助函数:

function isDirectInstanceOf(object, constructor) {
    return Object.getPrototypeOf(object) === constructor.prototype;
}

console.log(
    isDirectInstanceOf([], Array), // true
    isDirectInstanceOf([], Object), // false
);

这是正确的,但本质上与Mohammad、CRice和Paul已经给出的答案相同。 - Bergi
我非常感谢您的反馈。实际上,我认为我的答案仍然有价值,因为它建议了单独的功能,并且相当具有说明性和重点。您觉得呢? - devope
如果你想关注辅助函数,我会将其命名为 isDirectInstanceOf 或者(不太准确的)wasConstructedBy。"Child" 通常指的是子类。 - Bergi

-1
我创造了一个函数,用于检查DOM类和元素之间的关系。
const getCreator = instance => Object.getPrototypeOf(instance).constructor.name;
// usage
getCreator(document.documentElement) === "HTMLHtmlElement;

-2
问题在于你引用的所有类都是Foo的子类。例如,new Baz() instanceOf Bar && new Bar() instanceOf Foo === true。 因此,当你询问Bar是否是Foo的实例时,它将通过继承而成为真实的。

由于JS中没有Java getClass()等效方法,你应该使用类似以下的方法:

if (value.constructor.name !== Foo.name)

Java中的getClass()和JS中的value.constructor有什么不同? - Bergi
4
永远不要比较类的名称。它们可能会被缩小,可能会被优化掉,可能会被覆盖或伪造。永远不要依赖名称。 - Bergi
@bergi,你能否请检查一下我的答案? - devope

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接