for..in, hasOwnProperty(), and inheritance

# Felipe Gasper (14 years ago)

There is a widespread practice of doing this:


for (key in obj) { if (obj.hasOwnProperty(key)) { … } }

The oft-stated purpose for this pattern is to weed out code that comes from Object.prototype. The result, though, is that we prevent iteration through any inherited properties, which seems like overkill for handling the original problem.

(Incidentally, I’m surprised that augmenting Object.prototype isn’t warned/deprecated in ES5 Strict. It seems far easier to get people to stop augmenting Obj.pro, which is likely to break all kinds of things, than to get everyone to filter every for..in loop. But, anyway.)

It’s especially unproductive because it works against prototypal inheritance patterns. e.g.:

var dog = { speak: function() { return "arf!" } }; var beagle = Object.create(dog); beagle.colors = ["white","black","brown"]; var my_dog = Object.create(beagle); my_dog.name = "Chip";

Note that filtering via hasOwnProperty() will prevent a for..in iteration from seeing either "colors" or "speak".

Another example: in YUI, it’s impossible to do this would-otherwise-be-useful pattern:

var base_config = { width: "600px" }; … var my_config = Object.create(base_config); my_config.visible = false; var widget = new Y.Widget(my_config);

In the example above, YUI will not see the “width” property because YUI rejects all inherited properties when it iterates through the configuration hash.

So, a solution I am considering for my own work defines two methods: Object.gave(giver, key, obj) Function.prototype.gave(key,obj)

They do what they look like: Object.gave checks if the “giver” really “gave” the “key”ed value to the “obj”ect. The Function.prototype version does the same but assigns the function’s prototype as “giver”. (The original Object.gave() offloads to the prototype method if called with just two args.)

Thus:

var HOP = Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty); Object.gave = function(giver,key,obj) { if (arguments.length === 2) { Function.prototype.gave.apply(this,arguments); }

 var last_prototype;
 while ( obj !== giver ) {
     if (HOP(obj,key) || (obj === last_prototype)) return false;
     last_prototype = obj;
     obj = Object.getPrototypeOf(obj);
 }

 return true;

};

Function.prototype.gave = function(key,obj) { return Object.gave( this.prototype, key, obj ); };

Then, we can do:

for (var key in obj) { if (Object.gave(key,obj)) { … } }

…which will still filter out anything in Object.prototype, but will allow iteration through inherited properties.

This seems to me far more useful in general than the hasOwnProperty() check.

Thoughts?

-Felipe Gasper cPanel, Inc.

# Jake Verbaten (14 years ago)

Whats wrong with enumeration over own properties?

Object.keys() gets all enumerable own properties in a for .. in loop.

for .. in gets all enumerable properties in prototype chains.

I personally think it's bad practice to have code that enumerates over properties in the prototype chain. I can't see any good use case for it

# Axel Rauschmayer (14 years ago)

What’s the use case?

Don’t forget that whenever you set a property, you only ever modify the first object in the prototype chain.

The “own property” debate mainly exists, because objects are (ab)used as dictionaries. Then you don’t want inherited entries such as "toString".

# Felipe Gasper (14 years ago)

On 11/8/11 12:37 PM, Axel Rauschmayer wrote:

What’s the use case?

I thought I gave a pretty reasonable one before, but just in case:

In YUI, it’s impossible to use this otherwise-useful pattern:

var base_config = { width: "600px" }; … var my_config = Object.create(base_config); my_config.visible = false; var widget = new Y.Widget(my_config);

In the example above, YUI will not see the “width” property because YUI rejects all inherited properties when it iterates through the configuration hash.

Don’t forget that whenever you set a property, you only ever modify the first object in the prototype chain.

Right…that’s why gave() walks the prototype chain unless it finds the property on the object itself.

The “own property” debate mainly exists, because objects are (ab)used as dictionaries. Then you don’t want inherited entries such as "toString".

I disagree. That’s actually the crux of what I’m getting at.

IMO, you actually do want “inherited entries”; what you don’t want are specifically those things inherited from Object.prototype. Weeding out all inherited properties assumes that there is no legitimate use case for objects inheriting from other objects…which defeats the whole purpose of stuff like Object.create().

# Axel Rauschmayer (14 years ago)

What’s the use case?

I thought I gave a pretty reasonable one before, but just in case:

Thanks! Shorter is better... ;-)

In YUI, it’s impossible to use this otherwise-useful pattern:

var base_config = { width: "600px" }; … var my_config = Object.create(base_config); my_config.visible = false; var widget = new Y.Widget(my_config);

In the example above, YUI will not see the “width” property because YUI rejects all inherited properties when it iterates through the configuration hash.

Got it, you want to non-destructively modify base_config. It is kind of tricky to know when to stop traversing the prototype chain, so the more straightforward solution is to make a copy and modify there or to non-destructively merge:

developer.yahoo.com/yui/3/examples/yui/yui-merge.html

# Jake Verbaten (14 years ago)

You shouldnt store properties on the prototype chain like that. Your abusing Object.create as a shallow copy. Use a real shallow copy method.

On Nov 8, 2011 7:07 PM, "Felipe Gasper" <felipe at felipegasper.com> wrote:

On 11/8/11 12:37 PM, Axel Rauschmayer wrote: > > What’s the use case?

I thought I gave a pretty reasonable one before, but just in case:

In YUI, it’s impossible to use this otherwise-useful pattern:

--------- var base_config = { width: "600px" }; … var my_config = Object.create(base_config); my_co...

Don’t forget that whenever you set a property, you only ever modify the first object in the prot...

Right…that’s why gave() walks the prototype chain unless it finds the property on the object itself.

The “own property” debate mainly exists, because objects are (ab)used

as > dictionaries. Then ...

I disagree. That’s actually the crux of what I’m getting at.

IMO, you actually do want “inherited entries”; what you don’t want are specifically those things inherited from Object.prototype. Weeding out all inherited properties assumes that there is no legitimate use case for objects inheriting from other objects…which defeats the whole purpose of stuff like Object.create().

-FG

_______________________________________________ es-discuss mailing list es-discuss at mozilla.org http...

# Felipe Gasper (14 years ago)

On 11/8/11 1:17 PM, Axel Rauschmayer wrote:

What’s the use case?

In YUI, it’s impossible to use this otherwise-useful pattern:

var base_config = { width: "600px" }; … var my_config = Object.create(base_config); my_config.visible = false; var widget = new Y.Widget(my_config);

In the example above, YUI will not see the “width” property because YUI rejects all inherited properties when it iterates through the configuration hash.

Got it, you want to non-destructively modify base_config. It is kind of tricky to know when to stop traversing the prototype chain, …

Actually, have you ever seen a use case of wanting to prevent iteration through inherited properties other than Object.prototype? (Besides using for..in on Array objects.)

All of the examples I’ve ever seen of the problems that ensue from for..in iteration and prototypes stem from extending Object.prototype. Why not, then, specifically address that problem rather than preventing all iteration through inherited properties?

# Brendan Eich (14 years ago)

On Nov 8, 2011, at 11:48 AM, Felipe Gasper wrote:

Actually, have you ever seen a use case of wanting to prevent iteration through inherited properties other than Object.prototype? (Besides using for..in on Array objects.)

Sure. People make "classes" by defining function C(){} and decorating C.prototype.method1 = function(...){...}, etc. A for-in loop on (new C) will see all the methods, unlike the case with built-in constructors. That is an unwanted abstraction break: we should support abstracting over user-defined and built-in functions.

All of the examples I’ve ever seen of the problems that ensue from for..in iteration and prototypes stem from extending Object.prototype. Why not, then, specifically address that problem rather than preventing all iteration through inherited properties?

I think you're focusing too narrowly. I agree with Jake: make a copy into a single object, and access it (if it's a config dictionary) with a stylized API. Sounds like you could make YUI happy that way.

Separately, I think I'll propose for own(...) based on CoffeeScript's for own in/of loops. That seems more important for the long run than any API elaboration beyond Object.prototype.hasOwnProperty.

# Luke Smith (14 years ago)

Brendan Eich <mailto:brendan at mozilla.com> November 8, 2011 11:59 AM

On Nov 8, 2011, at 11:48 AM, Felipe Gasper wrote:

Actually, have you ever seen a use case of wanting to prevent iteration through inherited properties other than Object.prototype? (Besides using for..in on Array objects.)

Sure. People make "classes" by defining function C(){} and decorating C.prototype.method1 = function(...){...}, etc. A for-in loop on (new C) will see all the methods, unlike the case with built-in constructors. That is an unwanted abstraction break: we should support abstracting over user-defined and built-in functions.

I believe people decorate a class prototype directly because they always have, and because defineProperties wasn't available for them to declare prototype methods as not enumerable, not because they explicitly want class proto methods to be enumerable.

Can you elaborate on "we should support abstracting over user-defined and built-in functions"?

All of the examples I’ve ever seen of the problems that ensue from for..in iteration and prototypes stem from extending Object.prototype. Why not, then, specifically address that problem rather than preventing all iteration through inherited properties?

I think you're focusing too narrowly. I agree with Jake: make a copy into a single object, and access it (if it's a config dictionary) with a stylized API. Sounds like you could make YUI happy that way.

Copying loses the benefit of shared defaults via prototype. I would rather see a method to flatten an object before enumeration than lose the flexibility of shared state.

Luke

# Jake Verbaten (14 years ago)

Flexibility of shared state?

Are you really corrupting the state of defaults at runtime? Do you really want changes to your default options to propogate to all objects extending the defaults?

This seems like a right pain in the ass to debug/maintain?

On Nov 8, 2011 8:12 PM, "Luke Smith" <lsmith at lucassmith.name> wrote:


Brendan Eich <brendan at mozilla.com>

November 8, 2011 11:59 AM

On Nov 8, 2011, at 11:48 AM, Felipe Gasper wrote: > >> Actually, have

you ever seen a use case ...

I believe people decorate a class prototype directly because they always have, and because defineProperties wasn't available for them to declare prototype methods as not enumerable, not because they explicitly want class proto methods to be enumerable.

Can you elaborate on "we should support abstracting over user-defined and built-in functions"?

All of the examples I’ve ever seen of the problems that ensue from for..in iteration and prototypes stem from extending Object.prototype. Why not, then, specifically address that problem rather than preventing all iteration through inherited properties?

I think you're focusing too narrowly. I agree with Jake: make a copy

into a single object, and a...

Copying loses the benefit of shared defaults via prototype. I would rather see a method to flatten an object before enumeration than lose the flexibility of shared state.

Luke

# Brendan Eich (14 years ago)

On Nov 8, 2011, at 12:12 PM, Luke Smith wrote:

Sure. People make "classes" by defining function C(){} and decorating C.prototype.method1 = function(...){...}, etc. A for-in loop on (new C) will see all the methods, unlike the case with built-in constructors. That is an unwanted abstraction break: we should support abstracting over user-defined and built-in functions. I believe people decorate a class prototype directly because they always have, and because defineProperties wasn't available for them to declare prototype methods as not enumerable, not because they explicitly want class proto methods to be enumerable.

Yup, that's a fact. However it contradicts Felipe's belief that only Object.prototype might-be-enumerable properties need to be excluded, which was my point.

Can you elaborate on "we should support abstracting over user-defined and built-in functions"?

Abstract over means I can for-in an instance of either a built-in or (such a) user-defined constructor and not see methods. I can self-host or wrap Date without methods showing up.

Yes, ES5 supports Object.defineProperty etc. so I can make the WrappedDate.prototype methods non-enumerable, if I can target ES5. But the general point stands: enumeration up the prototype chain either is a bug, or you want no enumerable prototype properties (except perhaps those added by one privileged library), or you want a more elaborate protocol such as Felipe's gave/giver one.

Copying loses the benefit of shared defaults via prototype. I would rather see a method to flatten an object before enumeration than lose the flexibility of shared state.

That might be handy but Felilpe's gave/giver protocol still seems necessary in general (if you can't assume no one has decorated intermediate prototypes with enumerable properties that should not be considered).

The flattening idea reminds me of traits (traitsjs.org), since you might want different conflict-handling policies in case of shadowing. The default could shadow.

# Felipe Gasper (14 years ago)

On 11/8/11 2:19 PM, Jake Verbaten wrote:

Flexibility of shared state?

Are you really corrupting the state of defaults at runtime? Do you really want changes to your default options to propogate to all objects extending the defaults?

It’s the same thing as redefining a method inherited from a prototype.

Why would you extend the default at all if you don’t want changes to it to propagate to descendents? If you don’t want propagation, make a copy.

This seems like a right pain in the ass to debug/maintain?

In some cases, maybe; in other cases, I think not.

It also can better model what’s going on: if the schema conceives of multiple objects explicitly sharing the same default, then yes, you do want descendents of the parent to stay in sync with the parent (unless the descendents themselves have set values). That seems very intuitive to me.

If you want a descendent to be independent of the parent, then don’t make it a descendent; make it a copy.

FWIW, the YUI thing was pretty unintuitive to me and took me a while to “debug”.

# Felipe Gasper (14 years ago)

On 11/8/11 2:19 PM, Brendan Eich wrote:

On Nov 8, 2011, at 12:12 PM, Luke Smith wrote:

Sure. People make "classes" by defining function C(){} and decorating C.prototype.method1 = function(...){...}, etc. A for-in loop on (new C) will see all the methods, unlike the case with built-in constructors. That is an unwanted abstraction break: we should support abstracting over user-defined and built-in functions. I believe people decorate a class prototype directly because they always have, and because defineProperties wasn't available for them to declare prototype methods as not enumerable, not because they explicitly want class proto methods to be enumerable.

Yup, that's a fact. However it contradicts Felipe's belief that only Object.prototype might-be-enumerable properties need to be excluded, which was my point.

Wasn’t so much a belief as an observation that the reasons most folks give for wanting the hasOwnProperty() check in for..in loops are similar to what Crockford wrote here: www.yuiblog.com/blog/2006/09/26/for-in-intrigue

…i.e., the “main” concern, esp. if you inherit objects from just anywhere (e.g., YUI’s config objects), is Object.prototype.

The case Brendan mentions is closer to what I had in mind as a reason for the more fine-grained control of gave(). In Brendan’s case:

function C(){}; C.prototype.foo = function(){}; var my_c = new C(); my_c.bar = null;

…it makes sense to weed out stuff from the prototype. Even then, though, it might be equally, or more, gainful to check for:

for (key in my_c) { if ( !C.gave(key,my_c) ) { … } }

…or, at least, there could be multiple use cases, e.g. multiple levels of inheritance, and I want to filter out either just the original (gave/giver) or all prototypes (hasOwnProperty()).

Anyhow. Thanks to everyone for the bandwidth on the idea.

# David Bruant (14 years ago)

Le 08/11/2011 08:59, Felipe Gasper a écrit :

Hi everyone,

There is a widespread practice of doing this:

for (key in obj) { if (obj.hasOwnProperty(key)) { … } }

The oft-stated purpose for this pattern is to weed out code that comes from Object.prototype. The result, though, is that we prevent iteration through any inherited properties, which seems like overkill for handling the original problem.

(...)

So, a solution I am considering for my own work defines two methods: Object.gave(giver, key, obj) Function.prototype.gave(key,obj)

They do what they look like: Object.gave checks if the “giver” really “gave” the “key”ed value to the “obj”ect. The Function.prototype version does the same but assigns the function’s prototype as “giver”. (The original Object.gave() offloads to the prototype method if called with just two args.)

Thus:

var HOP = Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty); Object.gave = function(giver,key,obj) { if (arguments.length === 2) { Function.prototype.gave.apply(this,arguments); }

var last_prototype;
while ( obj !== giver ) {
    if (HOP(obj,key) || (obj === last_prototype)) return false;
    last_prototype = obj;
    obj = Object.getPrototypeOf(obj);
}

return true;

};

Function.prototype.gave = function(key,obj) { return Object.gave( this.prototype, key, obj ); };

Then, we can do:

for (var key in obj) { if (Object.gave(key,obj)) { … } }

…which will still filter out anything in Object.prototype, but will allow iteration through inherited properties.

This seems to me far more useful in general than the hasOwnProperty() check.

Thoughts?

Specifically regarding not enumerating Object.prototype properties, why not make every Object.prototype property non-enumerable at the beginning of your program?

for(p in Object.prototype) Object.defineProperty(Object.prototype, p, {enumerable:false});

Afterward, for any for..in loop, all properties (own or inherited) of your object are enumerated except the Object.prototype ones. It makes less properties to iterate over, it removes the .hasOwnProperty or Object.gave test. It only break scripts which rely on and expect Object.prototype properties being enumerated in a for..in loop (which I am not sure there are many out there)

If you ever care about enumerating Object.prototype properties, Object.getOwnPropertyNames is still here.

Of course, I do not cover the case Function.prototype.gave covers.