为什么使用Object.prototype.hasOwnProperty.call(myObj,prop)代替myObj.hasOwnProperty(prop)?


104

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

在阅读require.js的源代码时,我偶然发现了该函数:

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

hasOwn是对的引用Object.prototype.hasOwnProperty。将此功能编写为

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

既然如此,我们为什么要完全定义此功能?是否只是为了获得(略微)提高性能而使用快捷方式和对属性访问进行本地缓存的问题,还是我缺少在没有此方法的对象上使用hasOwnProperty的情况?

Answers:


109

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

用户可能有一个用创建的JavaScript对象Object.create(null),该对象将具有一个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对象(但不能保证宿主对象都可以遵循此规则,请参见RobG的深入说明Object.prototype在链上拥有其最后一个对象null(当然,返回的对象除外Object.create(null))。


您的逻辑可能是正确的,但我认为您很友善。如果require.js的作者认为hasOwnProperty可能已被覆盖(这是极不可能的),那么他们应该以这种方式调用所有内置方法(也许这样做)。
RobG 2012年

@Periback真的吗?我很确定它确实支持它。
Alex 2015年

ES6快捷方式(如果经常使用)。const hasProp = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop)
理查德·阿约特

15

如果我理解正确,那么Javascript中的每个对象都是从Object原型继承的

看起来似乎有点麻烦,但是javascript(ECMAScript实现的通用术语)和ECMAScript(用于javascript实现的语言)之间是有区别的。定义继承方案的是ECMAScript,而不是javascript,因此仅本机ECMAScript对象需要实现该继承方案。

运行中的javascript程序至少包含内置ECMAScript对象(对象,函数,数字等),并且可能还包含一些本机对象(例如函数)。它还可能具有某些主机对象(例如浏览器中的DOM对象或其他主机环境中的其他对象)。

尽管内置对象和本机对象必须实现ECMA-262中定义的继承方案,但主机对象却不能。因此,并非javascript环境中的所有对象都必须继承自Object.prototype。例如,在IE中实现为ActiveX对象的宿主对象如果被视为本机对象,则会引发错误(因此,为什么使用try..catch初始化MS XMLHttpRequest对象)。如果将某些DOM对象(如怪癖模式下IE中的NodeLists)传递给Array方法,则会抛出错误,IE 8及更低版本中的DOM对象没有类似ECMAScript的继承方案,依此类推。

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

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

至少对于quirks模式下的IE中的某些主机对象(以及IE 8及以下版本)至少不是这样。

鉴于以上所述,值得深思的是,为什么一个对象可能具有自己的hasOwnProperty方法,以及在不首先测试这是否是个好主意的情况下调用其他hasOwnProperty方法的可取性。

编辑

我怀疑使用的原因Object.prototype.hasOwnProperty.call是在某些浏览器中,宿主对象没有hasOwnProperty方法,可以使用call和内置方法来替代。但是,出于上述原因,一般而言这样做似乎不是一个好主意。

在涉及宿主对象的地方,通常可以使用in运算符来测试属性,例如

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

// false in most browsers, throws an error in IE 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');

替代方法(在IE6和其他版本中经过测试):

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中不起作用(至少在我的情况下)。所以我决定使用(o中的'bar')-它有所帮助。
最多

@Max in不执行hasOwnProperty()查找,我怀疑您要查找的属性已存在于原型链中。
Alex

这是来自eslint.org/docs/rules/no-prototype-builtins的有趣示例:例如,对于网络服务器来说,解析来自客户端的JSON输入并hasOwnProperty直接调用生成的对象是不安全的,因为恶意客户端可能发送类似的JSON值,{"hasOwnProperty": 1}并导致服务器崩溃。
11:11

可以,但是明智的做法是使用JSON模式测试或验证任何客户端提供的JSON以防止此类问题,即使您仅关注数据质量也是如此。而且它不应该导致服务器崩溃。:-)
RobG

8

JavaScript不保护属性名称hasOwnProperty

如果存在对象可能具有使用该名称的属性的可能性,则必须使用外部hasOwnProperty以获得正确的结果:

您可以将以下代码段粘贴粘贴到浏览器控制台,以更好地理解

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

总是返回假

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

使用另一个对象的hasOwnProperty,并调用它这个集为foo

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

为此,也可以使用对象原型中的hasOwnProperty属性

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

1
您要提出的要点已经在接受的答案中说明了,只是存在对hasOwnProperty收益的覆盖true
路易(Louis)

1

两个现有答案中给出的信息都可以找到。但是,使用:

('propertyName' in obj)

被提及几次。应当注意,hasOwnProperty仅当属性直接包含在要测试的对象上时,实现才会返回true。

in运营商将检查下来,通过原型链了。

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.