var objProto = { superProp: "abc" }; var obj = Object.create(objProto);obj is an object with no own properties whose prototype is objProto, an object with the property superProp. obj should be interpreted as an empty map. Let’s see what operations fail to do so.
Checking whether a property exists. The in operator checks whether an object has a property with a given name, but it considers inherited properties:
> "foo" in obj false // ok > "toString" in obj true // not ok, inherited from Object.prototype > "superProp" in obj true // not ok, inherited from objProtoGiven that obj should be considered empty, we need the check to ignore inherited properties. hasOwnProperty() provides the necessary services:
> obj.hasOwnProperty("toString") falseCollecting property names. How do we find out all of the keys in obj interpreted as a map? for-in looks promising:
> for (propName in obj) console.log(propName) superPropAlas, it considers inherited enumerable properties. The reason that no properties of Object.prototype show up here is that all of them are non-enumerable:
> Object.prototype.propertyIsEnumerable("toString") falseObject.keys() lists only own properties.
> Object.keys(obj) []This method only returns enumerable properties. If you want to list all properties, you need to use Object.getOwnPropertyNames(). You can observe the difference by applying the methods to Object.prototype:
> Object.keys(Object.prototype) [] > Object.getOwnPropertyNames(Object.prototype) [ 'toString', 'hasOwnProperty', 'valueOf', ... ]Properties foo added by assigning to obj["foo"] or obj.foo are enumerable by default.
Getting a property value. The normal way of getting properties accesses all properties:
> obj["toString"] [Function: toString]There is no built-in way in JavaScript to only read own properties, but you can easily implement one yourself:
function getOwnProperty(obj, propName) { if (obj.hasOwnProperty(propName)) { return obj[propName]; } else { return undefined; } }With that function, the inherited property toString is ignored:
> getOwnProperty(obj, "toString") undefined
> getOwnProperty({ foo: 123 }, "foo") 123However, if one adds a property to obj whose name is "hasOwnProperty" then that property overrides the method Object.prototype.hasOwnProperty() and getOwnProperty() ceases to work:
> getOwnProperty({ hasOwnProperty: 123 }, "foo") TypeError: Property 'hasOwnProperty' of object #<Object> is not a functionThis problem can be fixed, by directly referring to hasOwnProperty(). This avoids going through obj to find it:
function getOwnProperty(obj, propName) { if (Object.prototype.hasOwnProperty.call(obj, propName)) { return obj[propName]; } else { return undefined; } }
Mark S. Miller mentions real-world implications of this pitfall, in the email “Why we need to clean up __proto__” (which inspired this post):
Think this exercise is academic and doesn't arise in real systems? As observed at a support thread, until recently, on all non-IE browsers, if you typed "__proto__" at the beginning of a new Google Doc, your Google Doc would hang. This was tracked down to such a buggy use of an object as a string map. (To avoid such problems, Caja is shifting to using StringMap.js, which does seem safe on all platforms.)