Update 2012-10-29: “Properties in JavaScript” is a general introduction to how properties work.
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 {} trueWith 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") falseHowever, 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 trueIf 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 trueThere 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: