为啥使用 Object.prototype.hasOwnProperty.call(myObj, prop) 而不是 myObj.hasOwnProperty(prop)?

Posted

技术标签:

【中文标题】为啥使用 Object.prototype.hasOwnProperty.call(myObj, prop) 而不是 myObj.hasOwnProperty(prop)?【英文标题】:Why use Object.prototype.hasOwnProperty.call(myObj, prop) instead of myObj.hasOwnProperty(prop)?为什么使用 Object.prototype.hasOwnProperty.call(myObj, prop) 而不是 myObj.hasOwnProperty(prop)? 【发布时间】:2012-08-14 14:30:42 【问题描述】:

如果我理解正确的话,javascript 中的每个对象都继承自 Object 原型,这意味着 JavaScript 中的每个对象都可以通过其原型链访问 hasOwnProperty 函数。

在阅读RequireJS'源代码时,我偶然发现了这个函数:

function hasProp(obj, prop) 
    return hasOwn.call(obj, prop);

hasOwn 是对Object.prototype.hasOwnProperty 的引用。把这个函数写成

function hasProp(obj, prop) 
    return obj.hasOwnProperty(prop);

既然我们在做,为什么要定义这个函数呢?这只是为了(轻微)性能提升而对属性访问进行快捷方式和本地缓存的问题,还是我错过了任何可能在没有此方法的对象上使用 hasOwnProperty 的情况?

【问题讨论】:

【参考方案1】:

[我的例子之间]有什么实际区别吗?

用户可能有一个使用Object.create(null) 创建的JavaScript 对象,该对象将有一个null [[Prototype]] 链,因此不会有hasOwnProperty() 可用。由于这个原因,使用您的第二种形式将无法正常工作。

这也是对Object.prototype.hasOwnProperty() 的更安全的引用(而且更短)。

你可以想象有人可能做过......

var someObject = 
    hasOwnProperty: function(lol) 
        return true;
    
;

如果 hasProp(someObject) 像您的第二个示例那样实现,这将导致失败(它会直接在对象上找到该方法并调用该方法,而不是委托给 Object.prototype.hasOwnProperty)。

但不太可能有人会覆盖 Object.prototype.hasOwnProperty 引用。

既然我们在做,为什么要定义这个函数呢?

见上文。

是吗 只是属性访问的快捷方式和本地缓存的问题 (轻微)性能提升...

理论上它可能会更快,因为不必遵循[[Prototype]] 链,但我怀疑这可以忽略不计,不是原因实现就是它的原因。

...或者我错过了任何情况 hasOwnProperty 可能用在没有这个方法的对象上?

hasOwnProperty() 存在于Object.prototype,但可以被覆盖。每个原生 JavaScript 对象(但不保证宿主对象遵循这一点,see RobG's in-depth explanation)在链上 null 之前的最后一个对象是 Object.prototype(当然,Object.create(null) 返回的对象除外)。

【讨论】:

您的逻辑可能是正确的,但我认为您很友善。如果 require.js 的作者认为 hasOwnProperty 可能已被覆盖(这极不可能),那么他们应该以这种方式调用所有内置方法(也许他们会这样做)。 @Periback 真的吗?我很确定它确实支持它。 ES6 快捷方式(如果经常使用)。 const hasProp = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop)【参考方案2】:

如果我理解正确的话,JavaScript 中的每一个对象都继承自 Object 原型

这可能看起来像分裂头发,但 JavaScript(ECMAScript 实现的通用术语)和 ECMAScript(用于 JavaScript 实现的语言)之间是有区别的。定义继承方案的是 ECMAScript,而不是 JavaScript,因此只有原生 ECMAScript 对象需要实现该继承方案。

一个正在运行的 JavaScript 程序至少由内置的 ECMAScript 对象(对象、函数、数字等)和可能的一些本地对象(例如函数)组成。它也可能有一些宿主对象(例如浏览器中的 DOM 对象,或其他宿主环境中的其他对象)。

虽然内置对象和本机对象必须实现 ECMA-262 中定义的继承方案,但宿主对象不需要。因此,并非 JavaScript 环境中的所有对象都必须Object.prototype 继承。例如,在 Internet Explorer 中实现为 ActiveX 对象的主机对象如果被视为本机对象,则会引发错误(因此 try..catch 用于初始化 Microsoft XMLHttpRequest 对象)。某些 DOM 对象(例如 Internet Explorer 中的怪癖模式中的 NodeLists)如果传递给 Array 方法会引发错误,Internet Explorer 8 及更低版本中的 DOM 对象没有类似 ECMAScript 的继承方案,等等。

因此,不应假定 JavaScript 环境中的所有对象都继承自 Object.prototype。

这意味着 JavaScript 中的每个对象都可以通过其原型链访问 hasOwnProperty 函数

至少对于 Internet Explorer 中处于 quirks 模式(以及 Internet Explorer 8 及更低版本)中的某些主机对象而言,情况并非如此。

鉴于上述情况,值得思考为什么一个对象可能有自己的 hasOwnProperty 方法,以及调用其他一些 hasOwnProperty 方法而不首先测试是否是好主意与否。

我怀疑使用Object.prototype.hasOwnProperty.call的原因是在某些浏览器中,宿主对象没有hasOwnProperty方法,使用call和内置方法是另一种选择。但是,出于上述原因,通常这样做似乎不是一个好主意。

对于宿主对象,in 操作符可用于一般测试属性,例如

var o = document.getElementsByTagName('foo');

// false in most browsers, throws an error in Internet Explorer 6, and probably 7 and 8
o.hasOwnProperty('bar');

// false in all browsers
('bar' in o);

// false (in all browsers? Do some throw errors?)
Object.prototype.hasOwnProperty.call(o, 'bar');

另一种选择(在Internet Explorer 6 和其他人中测试过):

function ownProp(o, prop) 

  if ('hasOwnProperty' in o) 
    return o.hasOwnProperty(prop);

   else 
    return Object.prototype.hasOwnProperty.call(o, prop);
  

这样你只在对象没有它(继承或其他方式)的地方专门调用内置 hasOwnProperty

但是,如果一个对象没有hasOwnProperty 方法,那么使用 in 运算符可能同样适合,因为该对象可能没有继承方案并且所有属性都是在对象上(虽然这只是一个假设),例如in 运算符是测试 DOM 对象对属性的支持的一种常见(并且看似成功)的方法。

【讨论】:

谢谢。 Object.prototype.hasOwnProperty.call(o, 'bar') 在 FF 18.0 中不起作用(至少在我的情况下)。所以我决定使用 ('bar' in o)——它有帮助。 @Max in 不执行 hasOwnProperty() 查找,我怀疑您要查找的属性存在于原型链上。 这是来自eslint.org/docs/rules/no-prototype-builtins 的一个有趣示例:例如,Web 服务器解析来自客户端的 JSON 输入并直接在结果对象上调用 hasOwnProperty 是不安全的,因为恶意客户端可以发送像"hasOwnProperty": 1 这样的 JSON 值并导致服务器崩溃。 当然,但最好使用 JSON 模式测试或验证任何客户端提供的 JSON 以防止此类问题,即使您关心的只是数据质量。它不应该导致服务器崩溃。 :-)【参考方案3】:

JavaScript 不保护属性名称 hasOwnProperty

如果一个对象可能有一个具有此名称的属性,则有必要使用外部 hasOwnProperty 来获得正确的结果:

您可以将以下代码 sn-ps 复制粘贴到您的浏览器控制台以更好地理解

var foo = 
  hasOwnProperty: function() 
    return false;
  ,
  bar: 'I belong to foo'
;

总是返回假

foo.hasOwnProperty('bar'); // false

使用另一个对象的 hasOwnProperty 并在 this 设置为 foo 的情况下调用它

().hasOwnProperty.call(foo, 'bar'); // true

也可以为此目的使用 Object 原型中的 hasOwnProperty 属性

Object.prototype.hasOwnProperty.call(foo, 'bar'); // true

【讨论】:

您所提出的观点已经在accepted answer 中提出,除了hasOwnProperty 的覆盖返回true【参考方案4】:

前两个答案(按日期)中给出的信息都是正确的。但是,使用:

('propertyName' in obj)

被多次提及。需要注意的是,hasOwnProperty 实现只有在属性直接包含在被测试对象上时才会返回 true。

in 运算符也将向下检查原型链。

这意味着实例属性在传递给 hasOwnProperty 时将返回 true,而原型属性将返回 false。

使用 in 运算符,实例和原型属性都将返回 true。

【讨论】:

【参考方案5】:

除了这里的其他答案,请注意您可以使用新方法Object.hasOwn(大多数浏览器都支持,并且很快将在其余浏览器中支持)而不是Object.hasOwnProperty.call,因为它允许您编写更简洁的代码。

更多关于Object.hasOwn - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn

浏览器兼容性 - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn#browser_compatibility

【讨论】:

【参考方案6】:

这样更简单:

let foo = Object.create(null);
if (foo.bar != null) 
    console.log('object foo contains bar property in any value and 
    type, except type undefined and null');
    // bar property can exist in object foo or any object of the prototype chain

【讨论】:

请在您的回答中提供更多详细信息。正如目前所写的那样,很难理解您的解决方案。

以上是关于为啥使用 Object.prototype.hasOwnProperty.call(myObj, prop) 而不是 myObj.hasOwnProperty(prop)?的主要内容,如果未能解决你的问题,请参考以下文章

为啥使用 glTranslatef?为啥不直接更改渲染坐标?

为啥或为啥不在 C++ 中使用 memset? [关闭]

为啥在参数周围使用 /*、*/ 以及为啥在提取数组长度时使用 >>>? [复制]

为啥我们使用 hadoop mapreduce 进行数据处理?为啥不在本地机器上做呢?

为啥 CoreGui Roblox 锁定在 DataModel 中,为啥受信任的用户不能使用 CoreScripts?

为啥有人应该在 git commit 之前使用 git add?或者为啥有人应该使用 git add 呢?