3

在 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方法,所以即使对象没有自己的属性,in操作符也会返回。truetoString

这就是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...

为了解决这个问题,someObject.hasOwnProperty我们可以直接引用该方法,而不是使用Object.prototype

const hasOwn = Object.prototype.hasOwnProperty;
console.log(hasOwn.call(someObject, 'theAnswer')); // true

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


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

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

3 回答 3

5

in是运营商,所以不能被劫持。您不必依赖没有更改或隐藏脚本的Object, Object.prototype, Object.prototype.hasOwnProperty, Object.prototype.hasOwnProperty.call.

这是了解对象是否具有某些属性的快速方法。我的意思是,如果obj.foo可以返回,例如,"bar"即使该属性是继承的,那么能够事先知道是否具有该属性foo是有意义的,无论是拥有还是继承。objfoo

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

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

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

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

于 2016-10-31T02:35:12.203 回答
4

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

于 2016-10-31T02:31:44.090 回答
3

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

使用in运算符非常适合评估是否应该精确加载/执行 JavaScript polyfill,因为它会检查原型链。

例如:

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

相比:

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

因此,Polyfill.io 服务将其用于特征检测测试。

于 2016-10-31T02:32:10.937 回答