JavaScript 对象检测:点语法与“in”关键字

IT技术 javascript properties
2021-02-28 00:08:22

我见过两种检测 UA 是否实现特定 JS 属性的方法:if(object.property)if('property' in object).

我想听听关于哪个更好的意见,最重要的是,为什么。一个明显优于另一个吗?除了这两种方法可以进行对象属性检测吗?请涵盖浏览器支持、陷阱、执行速度等,而不是美学。

编辑: 鼓励读者在jsperf.com/object-detection上运行测试

5个回答
  • if(object.property)

    将在未设置的情况下失败(这是你想要的),在情况下,它已经被设置为一些falsey值,例如undefinednull0等(这是不是你想要的)。

    var object = {property: 0};
    if(object.isNotSet) { ... } // will not run
    if(object.property) { ... } // will not run
    
  • if('property' in object)

    稍微好一点,因为它实际上会返回对象是否真的具有该属性,而不仅仅是通过查看它的值。

    var object = {property: 0};
    if('property' in object) { ... } // will run
    if('toString' in object) { ... } // will also run; from prototype
    
  • if(object.hasOwnProperty('property'))

    甚至更好,因为它可以让您区分实例属性和原型属性。

    var object = {property: 0};
    if(object.hasOwnProperty('property')) { ... } // will run
    if(object.hasOwnProperty('toString')) { ... } // will not run
    

我会说性能在这里不是什么大问题,除非您每秒检查数千次,但在这种情况下,您应该考虑另一种代码结构。最近的浏览器都支持所有这些函数/语法,hasOwnProperty并且已经存在很长时间了。


编辑:您还可以通过将任何东西(甚至不是对象的东西)作为这样的对象传递来创建一个通用函数来检查属性是否存在:

function has(obj, prop) {
    return Object.prototype.hasOwnProperty.call(obj, prop);
}

现在这有效:

has(window, 'setTimeout'); // true

即使window.hasOwnProperty === undefined(在 IE 版本 8 或更低版本中就是这种情况)。

@Ben Lee:我认为你是对的。但是,调用函数通常会比较慢,所以我不完全确定。
2021-05-04 00:08:22
@Ben Lee:结果在 Chrome 上有所不同:jsperf.com/object-detection
2021-05-12 00:08:22
object.hasOwnProperty如果object是宿主对象,则很危险,因为宿主对象没有义务继承Object.prototype(实际上在 IE < 9 中,它们通常不会),因此我建议不要使用该对象。
2021-05-13 00:08:22
我同意性能考虑在这里无关紧要(即使对于数十万次比较,差异也不重要,因为速度太快了)。但是从学术上思考,如果有什么if (object.hasOwnProperty)最快的,因为它只是检查内存中的对象的属性。另外两个还有更多的工作要做。if ('property' in object)已经检查了对象中的属性,如果没有找到属性,还要检查原型链。if (object.property)必须检查对象中的属性,然后在大多数情况下还要进行类型强制。
2021-05-15 00:08:22
@Tim Down:谢谢,很高兴知道。我会试着摆弄一下,看看 IE 是如何处理它的。
2021-05-17 00:08:22

这实际上取决于您想要实现的目标。您是在谈论主机对象(例如window和 DOM 节点)吗?如果是这样,最安全的检查是typeof,它适用于我知道的所有主机对象:

 if (typeof object.property != "undefined") { ... }

笔记:

  • 避免object.hasOwnProperty()用于宿主对象,因为宿主对象没有义务继承Object.prototype,因此可能没有hasOwnProperty()方法(实际上在 IE < 9 中,它们通常没有)。
  • 简单的布尔强制转换(例如if (object.property) { ... })是对属性存在性的糟糕测试,因为它会为假值提供假阴性。例如,对于一个空的 textarea,if (textarea.selectionStart) { ... }即使该属性存在也不会执行该块。此外,当尝试强制转换为布尔值(例如var xhr = new ActiveXObject("Microsoft.XMLHTTP"); if (xhr.responseXML) { ... }时,某些宿主对象属性会在旧版本的 IE 中引发错误
  • in操作是一个属性的存在的一个更好的测试,但再次没有关于主机对象对它的支持保障。
  • 我建议不要考虑此类任务的性能。为您的项目选择最安全的选项,然后再进行优化。几乎肯定会有比属性存在检查更好的优化候选者。

有关这方面的更多背景信息,我推荐Peter Michaux 这篇出色的文章

@Ben Lee:我不确定它是否会抛出错误,但它对于宿主对象并不完全可靠。它不适用于在 Firefox 中检测事件处理程序属性,例如:"onmouseover" in document返回false支持实际上似乎比我记得的要好,通过谷歌搜索判断:我能找到的最好的是在评论中提到 kangax 对以下内容:nczonline.net/blog/2010/07/27/...
2021-04-26 00:08:22
@Ben Lee:另外,查看 ECMAScript 3 规范的第 8.6.2 节(这仍然是当今普遍使用的浏览器集的基线),它特别指出宿主对象可以选择实现或不实现任何内部方法它喜欢,包括[[HasProperty]](这是in运营商所依赖的)。简而言之,规范似乎在说没有保证。
2021-05-08 00:08:22
您确定该in运算符不一定可用于宿主对象吗?毕竟它是一个操作对象的操作符。而且我认为 javascript 中的所有对象,包括宿主对象,基本上仍然是属性/值对的集合,所以应该应用 in 运算符,对吗?
2021-05-14 00:08:22

绝对if ('property' in object)是正确的方法。这实际上测试属性是否在对象中(或在其原型链中,更多内容见下文)。

if (object.property)另一方面,会将“财产”强制转换为真值/flash值。如果未设置该属性,它将返回“未定义”,这将被强制为 false,并且似乎可以正常工作。但是对于许多其他属性设置值,这也将失败。众所周知,javascript 在将其视为真和假方面不一致。

最后,就像我上面说的,'property' in 'object'如果它在原型链的任何地方就会返回 true。如果你想在对象本身上测试它,而不是在链中更高的某个地方,你可以使用这样的hasOwnProperty方法:

if (object.hasOwnProperty('property')) ...

如果“property”为 0,则第一个将失败。要确保确实存在某个属性,您需要检查该属性object.property !== undefined,或使用 in 关键字。

[编辑]

还有hasOwnProperty-function,但我从来没有真正使用过那个,所以我不能说太多。虽然我认为如果属性设置在原型中,它不会返回 true,有时你想要,有时你不想要。

属性可以存在且未定义。(如果你想测试不确定性,那么typeof object.property === "undefined"更安全)。
2021-04-22 00:08:22
要删除属性,您可以 delete object.property
2021-05-05 00:08:22
将属性设置为 undefined 与删除它不一样吗?或者,如果您迭代属性,它仍然会显示吗?
2021-05-11 00:08:22
@Alexandr,不,不一样。尝试a = {}; a.x = undefined; a.hasOwnPropety('x') == true; a.hasOwnProperty('y') == false;
2021-05-17 00:08:22
呵呵。不知道。这是关于将属性设置为 undefined 实际设置属性的部分。不过我知道 delete 关键字。只是认为他们做了同样的事情。
2021-05-17 00:08:22

这允许您将 window.hasOwnProperty 用作引用自身或其他内容,而不管您的脚本宿主如何。

// No enclosing functions here
if (!('hasOwnProperty' in this))
    function hasOwnProperty(obj, prop) {
        var method = Object.prototype.hasOwnProperty;
        if (prop === undefined)
            return method.call(this, obj);
        return method.call(obj, prop);
    }

//Example of use
var global = global || this; //environment-agnostic way to get the global object
var x = 'blah';
WScript.Echo(global.hasOwnProperty('x') ? 'true' : 'false'); //true

//Use as non-object method
var y = { z: false };
WScript.Echo(hasOwnProperty(y, 'z') ? 'true' : 'false'); //true
WScript.Echo(hasOwnProperty(y, 'w') ? 'true' : 'false'); //false

// true ಠ_ಠ
WScript.Echo(hasOwnProperty(global, 'hasOwnProperty') ? 'true' : 'false');