This post examines how inheritance and enumerability affect operations on properties in JavaScript.
Object.keys(obj)
Object.getOwnPropertyNames(obj)
obj.hasOwnProperty(propName)
Get property value:
Object.getOwnPropertyDescriptor(obj, propName)Set property values, delete properties (only affects the first object in the prototype chain):
obj.propName = value
obj["propName"] = value
delete obj.propName
delete obj["propName"]
Object.defineProperty(obj, propName, desc)
Object.defineProperties(obj, descObj)
for (propName in obj)
propName in objRead property value:
obj.propName
obj["propName"]
> "valueOf" in {}
true
> "toString" in {}
true
With ECMAScript 5, you use the operations from (1) and (2) and everything is OK.
> var proto = { foo: 123 };
> var obj = Object.create(proto);
> obj.hasOwnProperty("foo")
false
However, prior to ECMAScript 5, people often used the operations from (3) and (4) and that causes problems:
> for (var p in obj) console.log(p);
foo
> "foo" in obj
true
If you make the prototype property non-enumerable, you can fix the for-in loop, but not the in operator:
> var proto = {};
> Object.defineProperty(proto, "foo", { enumerable: false, value: 123 });
{}
> var obj = Object.create(proto);
> for (var p in obj) console.log(p);
> "foo" in obj
true
There are more challenges when it comes to using objects as maps, consult [2] for details.
/** Return an array with the names of the inherited enumerable properties of obj */
function inheritedEnumerablePropertyNames(obj) {
var result = [];
for (var propName in obj) {
result.push(propName);
}
return result;
}
/** Return an array with the names of the inherited properties of obj */
function inheritedPropertyNames(obj) {
if ((typeof obj) !== "object") { // null is not a problem
throw new Error("Only objects are allowed");
}
var props = {};
while(obj) {
Object.getOwnPropertyNames(obj).forEach(function(p) {
props[p] = true;
});
obj = Object.getPrototypeOf(obj);
}
return Object.getOwnPropertyNames(props);
}
Objects: all non-own properties are non-enumerable.
> inheritedPropertyNames({ foo: "abc" })
[ 'foo',
'constructor',
'toString',
...
'__lookupSetter__' ]
> inheritedEnumerablePropertyNames({ foo: "abc" })
[ 'foo' ]
Arrays: all non-own properties and length are non-enumerable.
> inheritedPropertyNames([ "abc" ])
[ '0',
'length',
'constructor',
'concat',
...
'__lookupSetter__' ]
> inheritedEnumerablePropertyNames([ "abc" ])
[ '0' ]
Note that this might give you the idea that you can use for-in to iterate over the indices in an array. However that is not recommended, because it won’t work properly if someone adds a (non-index) property to the array.
JavaScript programmers: