5

When is hasOwnProperty not required?

The book JavaScript: The Good Parts includes the following which says that "it is usually necessary":

The other form (called for in) enumerates the property names (or keys) of an object. On each iteration, another property name string from the object is assigned to the variable.

It is usually necessary to test object.hasOwnProperty(variable) to determine whether the property name is truly a member of the object or was found instead on the prototype chain.

for (myvar in obj) {
 if (obj.hasOwnProperty(myvar)) {
 ...
 }
}

More specifically I want to enumerate the properties of a simple dictionary-like object, which is created using Javsacript object literal syntax, for example:

var foo = { 'bar': 'baz' };

or a similar object which is created using JSON.parse:

var foo = JSON.parse("{ 'bar': 'baz' }");

Should I use hasOwnProperty when I do for in on the foo object?

Assume that this javascript is running in a random browser as part of a complex web page.

Is the answer, "In practice it is probably/usually not necessary. In theory it could be necessary if some framework or library added a property by changing Object.prototype, but changing Object.prototype like that would be an intrusive bad practice which any respectable framework is unlikely to do"?

ChrisW
  • 54,973
  • 13
  • 116
  • 224
  • 1
    You can do `Object.keys(obj).forEach(key => ...)` as it will get own properties only. – elclanrs Jan 08 '17 at 23:59
  • I too would recommend `Object.keys` - with appropriate [polyfill](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/keys#Polyfill) if you think you'll need to support IE8 or earlier – Jaromanda X Jan 09 '17 at 00:05
  • I believe some web pages _do_ change `Object.prototype`, or something similarly important. I once found when my JavaScript was run inside SalesForce, that `for.. in` iterated over things other than just the intended items of my data structure (can't remember whether this was an Array or Object though). – Birchlabs Jan 09 '17 at 00:08
  • @Birchlabs if you're writing your own code, you can probably tell if the libraries you're using are going to cause you this problem or not – Alnitak Jan 09 '17 at 00:09
  • @Alnitak I tested it on my development machine and found that I didn't need it (i.e. libraries that I'm using don't currently cause a problem). I don't know about other users' browsers though. Who knows, might some browser extension, for example, mess things up? – ChrisW Jan 09 '17 at 00:18
  • @ChrisW if a browser extension could do that (I don't know) it would break every site that uses jQuery. It wouldn't last long in that state. – Alnitak Jan 09 '17 at 00:18
  • @Alnitak I think that a browser extension could inject 3rd-party JavaScript into every web page. But are you saying that jQuery depends on `hasOwnProperty` not being required? – ChrisW Jan 09 '17 at 00:23
  • I agree with Alnitak: if you control the construction of the objects (so you know that they don't have complex prototype properties) and you control the environment (so you know there aren't any libraries adding enumerable properties to common prototypes) then you're safe enough skipping it. Otherwise, you might use the `Object.keys` suggestion below, in some guise. – Scott Sauyet Jan 09 '17 at 00:23
  • @ChrisW no, I'm saying that jQuery depends on people not adding enumerable properties to `Object.prototype`. – Alnitak Jan 09 '17 at 00:23
  • Ah, for what it's worth I found the code that was unsafe. Essentially it was `var x = ["str"]; for(var index in x) { var item = x[index]; }` In many webpages this ran fine, but we found that it wasn't always safe. `for.. in` is _not_ exactly the same as "iterate through this array". If `Array.prototype` or `Object.prototype` have been modified, then `for.. in` will iterate over the extra properties, and not just the array's contents. I suspect that the environment in which my code was run, had modified `Array.prototype` to give it some helper functions. `for..in` is sensitive to these. – Birchlabs Jan 09 '17 at 00:23
  • 1
    @Birchlabs exactly that yes, but in ES5+ that code should be using `Object.defineProperty` to make those additions _non-enumerable_ so that they _don't_ appear when you do `for ... in` – Alnitak Jan 09 '17 at 00:25
  • @Alnitak Thank you too for that info about jQuery. From that I found this as a related topic: [Prototyping Object in Javascript breaks jQuery?](http://stackoverflow.com/q/1827458/49942) – ChrisW Jan 09 '17 at 00:28
  • @ChrisW I'd say the top answer there is no longer correct. – Alnitak Jan 09 '17 at 00:33
  • @Alnitak Which is the "top" answer there, and how is it no longer correct? – ChrisW Jan 09 '17 at 00:39
  • 1
    @ChrisW the one that says "Don't extend `Object.prototype`". That was correct then, but these days (_when done correctly_) it's perfectly safe. – Alnitak Jan 09 '17 at 00:42

3 Answers3

5

IMHO, in modern JS interpreters it's almost[*] never required, and especially not for a plain object that's just being used as a dictionary.

jQuery manages just fine without it, because people have learnt that unsafely adding enumerable properties to Object.prototype is a bad idea.

ES5, of course, allows you to add non-enumerable properties to Object.prototype if you really want to, using Object.defineProperty.

[*] The exception to the above is if you're writing code that's specifically examining the prototype chain and really needs to know whether an enumerable property is inherited or not

Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • 1
    Was it "usually necessary" for some reason in 2008, when the book was written? – ChrisW Jan 09 '17 at 00:14
  • @ChrisW most likely, yes - back then folks _did_ add stuff to `Object.prototype` and you didn't have the option to create non-enumerable properties – Alnitak Jan 09 '17 at 00:15
4

hasOwnProperty is not required when iterating a plain object2 in a "sane, modernish, environment"1: this assumption means restricting legacy browser support.

All of the standard Object properties have been non-enumerable for a very long time and new core methods have always been non-enumerable.


1 If code decides to add to Object.prototype, which is questionable enough aside from polyfills, it SHOULD add it as a non-enumerable property. Adding a new enumerable property violates the "sane environment" constraint. IE 9+ (and FF/Chrome/Safari etc.) support Object.defineProperty sufficiently for this task. IE 8 does not and violates the "modernish" constraint as well.

2 Arrays do not qualify as plain objects. Using for..in is also ill-advised for most array iteration.

user2864740
  • 60,010
  • 15
  • 145
  • 220
  • 1
    Agreed - the book advice could be relevant in the context of using polyfills which add enumerable properites to `Object.prototype`. Of course adding a polyfill for [Object.keys](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/keys#Polyfill) (which calls `hasOwnProperty` for you) works around this. – traktor Jan 09 '17 at 00:46
0

It's required only in absolutely predictable situations, that means - almost never.

In ECMAScript 5.1, Object.create was added, which enables the creation of objects with a specified [[Prototype]]. Object.create(null) is a common pattern used to create objects that will be used as a Map. This can lead to errors when it is assumed that objects will have properties from Object.prototype. This rule prevents calling some Object.prototype methods directly from an object.

Additionally, objects can have properties that shadow the builtins on Object.prototype, potentially causing unintended behavior or denial-of-service security vulnerabilities. For example, it would be unsafe for a webserver to parse JSON input from a client and call hasOwnProperty directly on the resulting object, because a malicious client could send a JSON value like {"hasOwnProperty": 1} and cause the server to crash.

To avoid subtle bugs like this, it's better to always call these methods from Object.prototype. For example, foo.hasOwnProperty("bar") should be replaced with Object.prototype.hasOwnProperty.call(foo, "bar").

user12367662
  • 547
  • 1
  • 4
  • 4