When is `hasOwnProperty` not required?
Asked Answered
F

3

6

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"?

Flare answered 8/1, 2017 at 23:58 Comment(15)
You can do Object.keys(obj).forEach(key => ...) as it will get own properties only.Doleful
I too would recommend Object.keys - with appropriate polyfill if you think you'll need to support IE8 or earlierJacinto
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).Detumescence
@Detumescence 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 notCelie
@Celie 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?Flare
@Flare 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.Celie
@Celie 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?Flare
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.Cobden
@Flare no, I'm saying that jQuery depends on people not adding enumerable properties to Object.prototype.Celie
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.Detumescence
@Detumescence 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 ... inCelie
@Celie Thank you too for that info about jQuery. From that I found this as a related topic: Prototyping Object in Javascript breaks jQuery?Flare
@Flare I'd say the top answer there is no longer correct.Celie
@Celie Which is the "top" answer there, and how is it no longer correct?Flare
@Flare the one that says "Don't extend Object.prototype". That was correct then, but these days (when done correctly) it's perfectly safe.Celie
C
6

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

Celie answered 9/1, 2017 at 0:6 Comment(2)
Was it "usually necessary" for some reason in 2008, when the book was written?Flare
@Flare most likely, yes - back then folks did add stuff to Object.prototype and you didn't have the option to create non-enumerable propertiesCelie
B
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.

Bodleian answered 9/1, 2017 at 0:25 Comment(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 (which calls hasOwnProperty for you) works around this.Maynard
C
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").

Colossians answered 30/4, 2020 at 18:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.