2016-10-31 38 views
3

在JavaScript中,in运算符检查对象是否具有指定的属性。但是,它不仅检查对象自己的属性,还检查原型链。因此,在某些情况下,它可能表现得不尽如人意。有什么情况下,我应该使用in运算符而不是hasOwnProperty()?

比方说,由于某种原因,我们有一个对象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,所以即使该对象没有自己的toString属性,in运算符也会返回true

这里的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 
+0

在大多数情况下,如果一个函数接受一些东西属性 - 它不应该是它的业务,其中在原型链中的属性定义,但它只是提供。这样你就可以将数据形状与实现细节分开。 – zerkms

回答

2

特征检测装载polyfills,测试条件使用现代化的DOM API等

使用in运营商的理想选择评估是否应该加载/精确执行一个JavaScript填充工具,因为它会检查原型链。

例如:

// 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用它来满足功能检测试验。

4

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

+1

我正在寻找特定用例,就像在amdouglas的答案中一样。 –

4

in是一个操作符,所以它不能被劫持。您不必依靠没有改变或遮蔽的脚本ObjectObject.prototype,Object.prototype.hasOwnProperty,Object.prototype.hasOwnProperty.call

而且它是知道对象是否具有某些属性的快速方法。我的意思是,如果obj.foo可以返回例如"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! :)

相关问题