for..in, hasOwnProperty(), and inheritance

Felipe Gasper felipe at felipegasper.com
Mon Nov 7 23:59:03 PST 2011


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.

(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.


More information about the es-discuss mailing list