在某些情况下我应该使用 in 运算符而不是 hasOwnProperty() 吗?

Are there any cases when I should use the in operator instead of hasOwnProperty()?

在 JavaScript 中,in 运算符检查对象是否具有指定的 属性。但是,它不仅检查对象自身的属性,还检查原型链。因此,在某些情况下,它的行为可能与预期不完全一致。

假设由于某种原因我们有一个对象 someArrayMethods 包含(显然)一些数组方法作为键:

const someArrayMethods = {
  indexOf: true,
  map: true,
};

我们可以使用 in 运算符检查该对象是否具有特定方法作为键:

console.log('indexOf' in someArrayMethods); // true
console.log('every' in someArrayMethods); // false

如果我们尝试检查 toString 属性 会怎样?

console.log('toString' in someArrayMethods); // true

惊喜!事实证明,这个对象在原型链中有一个 toString method,所以 in 运算符 returns true 即使对象没有自己的 toString 属性.

这就是 hasOwnProperty() 的救星!它与 in 运算符几乎相同,只有一个区别:它不检查原型链。我们可以重写我们之前的例子:

console.log(someArrayMethods.hasOwnProperty('toString'));  // false

现在它按预期工作了。不幸的是,hasOwnProperty() 在一种情况下也会失败。如果我们有一个带有自己的 属性 hasOwnProperty 的对象怎么办?看这个例子:

const someObject = {
  hasOwnProperty() {
    return false;
  },
  theAnswer: 42,
};

// Does `someObject` has own property `theAnswer`?
console.log(someObject.hasOwnProperty('theAnswer')); // false
// Well, it seems it doesn't...

为了解决这个问题,我们可以直接从Object.prototype:

中引用那个方法而不是使用someObject.hasOwnProperty
const hasOwn = Object.prototype.hasOwnProperty;
console.log(hasOwn.call(someObject, 'theAnswer')); // true

这似乎是检查对象是否具有某些 属性 的最合理方法。尽管如此,是否存在 in 运算符有用的情况?我知道它可以用来检查某个 class 的实例是否有某种方法,但在这种情况下,简单地检查该对象是否是那个 class 的实例不是更好吗?


作为旁注,另一种选择是使用 Object.keys() with ECMAScript 2016 Array.prototype.includes():

console.log(Object.keys(someObject).includes('theAnswer')); // true

你回答你自己的问题。 in 当你想在原型链中搜索时也很好。

用于加载 polyfill 的功能检测、使用现代 DOM API 的测试条件等。

使用 in 运算符是评估是否应该 load/execute 一个 JavaScript polyfill 的理想选择,因为它会检查原型链。

例如:

// this works wonderfully
if (!('addEventListener' in window)) {
  // polyfill addEventListener
}

相比于:

// this doesn't work at all
if (!window.hasOwnProperty('addEventListener')) {
  // polyfill addEventListener
}

因此 the Polyfill.io service 将其用于特征检测测试的原因。

in是一个运算符,所以不能被劫持。您不必依赖没有脚本被更改或隐藏 ObjectObject.prototypeObject.prototype.hasOwnPropertyObject.prototype.hasOwnProperty.call.

这是一种快速了解对象是否具有某些 属性 的方法。我的意思是,如果 obj.foo 可以 return 例如"bar" 即使 foo 属性 是继承的,能够事先知道 obj 是否具有 foo 属性 也是有意义的, 拥有或继承。

当然,如果我们只有 HasOwnProperty,我们可以(通常)继续调用 [[GetPrototypeOf]] 直到链结束,并检查每个对象。但这对代码来说会很乏味,可能比原生 in 慢,而且在 ES5 之前是不可能的。

而且,还有一个根本的区别。 in 运算符使用 [[HasProperty]] 内部方法,而 HasOwnProperty 使用 [[GetOwnProperty]]。对于非普通对象,迭代 [[GetOwnProperty]] 和 [[GetPrototypeOf]] 可能会产生与 [[HasProperty]] 不同的结果。

所以是的:当你想调用对象的内部 [[HasProperty]] 方法时,in 运算符很有用。事实上,除了Reflect.has,这是唯一正确的方法。

var p = new Proxy({}, {has: function() {
  console.log('Hooray!');
  return true;
}});
p.hasOwnProperty('foo'); // :(
'foo' in p; // Hooray! :)