Reputation: 52523
I'm trying to write a function that needs to know the property names of an object being passed in, like so:
var data = { "key1":"value1", "key2":"value2", etc}
^ i want the string value "key1"
How do I retrieve the string "key1" from data
? I know I can set a property dynamically like data[prop]=value
but i want to know what prop
is from an object passed in.
If that doesn't make sense I suppose I could try to explain more. Thanks!
I eventually want to do something like:
for (var i = 0; i<data.length; i++)
{
var name = data[i].getPropertyName() <--- not a real function
// do stuff
}
Upvotes: 3
Views: 19623
Reputation: 1074286
Before we look at our options, a quick note of the four key things about properties in JavaScript:
Symbol
s.a = ['x', 'y', 'z']
defines an array with three properties whose names are "0"
, "1"
, and "2"
. When we do a[0]
to access the first one, the number 0
is converted to a string. (In theory; again, the JavaScript implementation is allowed to optimize.)All of these properties can be discovered and enumerated (even the non-enumerable ones). You have several options for doing so:
for-in
loop (spec | MDN), with or without a hasOwnProperty
guard inside the loop to differentiate between "own" and inherited properties. (Does not include properties named with Symbol
s.) Loops through the names of the properties.Object.keys
(spec | MDN) (ES5+), which returns an array of the names of an object's own, enumerable properties. (Does not include properties named with Symbol
s.)Object.getOwnPropertyNames
(spec | MDN) (ES5+), which returns an array of the names of an object's own properties, regardless of whether they're enumerable. (Does not include properties named with Symbol
s.)Reflect.enumerate
(spec | MDN) (ES2015+), which returns an iterator for the names of the enumerable properties of an object, including ones it inherits. (Does not include properties named with Symbol
s.)Object.getOwnPropertySymbols
(spec | MDN) (ES2015+), which returns an array of the names of an object's own properties named with Symbol
s, regardless of whether they're enumerable. (Leaves out ones named with strings.)Reflect.ownKeys
(spec | MDN) (ES2015+), which returns an array of the names of an object's own properties no matter how they're named (Symbol
or string), and whether they're enumerable or not.As you can see, most of the operations only include properties whose names are strings, with only Object.getOwnPropertySymbols
and Reflect.ownKeys
giving us the ones named with Symbol
s.
The order of the keys is not defined (not even in ES2015) for for-in
or Object.keys
. In ES2015 and above, it is defined for the other four, by the [[OwnPropertyKeys]]
and (where applicable) [[Enumerate]]
operations. (Since ES2015 is still [as of this writing] relatively knew, it's possible not all JavaScript engines correctly implement the order yet.)
Let's look at examples. First, some setup:
// Create an object with one "own" property (plus the ones it
// inherits from Object.prototype):
var proto = {
one: 1
}
// Create an object that uses the above as its prototype
var obj = Object.create(proto);
// Add a couple of enumerable properties
obj.two = 2;
obj.three = 3;
// Add a non-enumerable property (by default, properties created
// with Object.defineProperty are non-enumerable)
Object.defineProperty(obj, "four", {
value: 4,
configurable: true,
writable: true
});
(Object.create
was added in ES5, but the version of it taking just one argument [as above] can easily be shimmed/polyfilled for obsolete JavaScript engines, like the one in IE8. Object.defineProperty
was also added in ES5, and cannot be correctly shimmed/polyfilled.)
Since most of the operations only involve properties named by strings, we're ignoring Symbol
s for now.
Once the code above runs, we have this in memory (the *
next to a name indicates it's a non-enumerable property):
+−−−−−−−−−−−−−−−−−+ Object.prototype−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+−−>| toString* |−−>(...a function...) | | valueOf* |−−>(...a function...) | | hasOwnProperty* |−−>(...a function...) | | ... | | +−−−−−−−−−−−−−−−−−+ | +−−−−−−−−−−−−−−−+ | proto−−−−−−−−−−−−−−−−−−−−+−−>| [[Prototype]] |−−+ | | one: 1 | | +−−−−−−−−−−−−−−−+ | +−−−−−−−−−−−−−−−+ | obj−−>| [[Prototype]] |−−+ | two: 2 | | three: 3 | | four*: 4 | +−−−−−−−−−−−−−−−+
With that setup, let's look at our options:
for-in
for-in
loops through the names of all of an object's properties (including ones it inherits from its prototype) whose names are strings (leaving out any whose names are Symbol
s).
for (var name in obj) {
// name is the name of each property, so:
console.log(name + " = " + obj[name]);
}
// Create an object with one "own" property (plus the ones it
// inherits from Object.prototype):
var proto = {
one: 1
}
// Create an object that uses the above as its prototype
var obj = Object.create(proto);
// Add a couple of enumerable properties
obj.two = 2;
obj.three = 3;
// Add a non-enumerable property (by default, properties created
// with Object.defineProperty are non-enumerable)
Object.defineProperty(obj, "four", {
value: 4,
configurable: true,
writable: true
});
for (var name in obj) {
// name is the name of each property, so:
console.log(name + " = " + obj[name]);
}
With that, we see
two = 2 three = 3 one = 1
...or similar. The order you see the properties in is not defined, not even in ES2015.
for-in
with a hasOwnProperty
guardIf we wanted the loop, but ignoring inherited properties, we can add a hasOwnProperty
check:
for (var name in obj) {
if (obj.hasOwnProperty(name)) {
console.log(name + " = " + obj[name]);
}
}
// Create an object with one "own" property (plus the ones it
// inherits from Object.prototype):
var proto = {
one: 1
}
// Create an object that uses the above as its prototype
var obj = Object.create(proto);
// Add a couple of enumerable properties
obj.two = 2;
obj.three = 3;
// Add a non-enumerable property (by default, properties created
// with Object.defineProperty are non-enumerable)
Object.defineProperty(obj, "four", {
value: 4,
configurable: true,
writable: true
});
for (var name in obj) {
if (obj.hasOwnProperty(name)) {
console.log(name + " = " + obj[name]);
}
}
With that, we see
two = 2 three = 3
We don't see one
because it's inherited.
Since hasOwnProperty
is a method on the object, in theory it could be overridden with a version that didn't return what we expect:
obj.hasOwnProperty = function() {
return true;
};
...which would fool our loop above. That's not usually an issue, but if you want to guard against it, use the one from Object.prototype
instead:
var hasOwn = Object.prototype.hasOwnProperty;
for (var name in obj) {
if (hasOwn.call(obj, name)) {
console.log(name + " = " + obj[name]);
}
}
Of course, someone can assign to the Object.prototype.hasOwnProperty
property as well, but if they do, a fair number of things are likely to break.
Object.keys
(ES5+, easily shimmed/polyfilled)Object.keys
gives us an array of the names of the object's own, enumerable properties named with strings. So it doesn't include inherited properties, properties marked non-enumerable, or properties named with Symbol
s.
var propNames = Object.keys(obj);
We can then loop over the entries in that array in any of several ways, such as forEach
:
propNames.forEach(function(name) {
console.log(name + " = " + obj[name]);
});
// Create an object with one "own" property (plus the ones it
// inherits from Object.prototype):
var proto = {
one: 1
}
// Create an object that uses the above as its prototype
var obj = Object.create(proto);
// Add a couple of enumerable properties
obj.two = 2;
obj.three = 3;
// Add a non-enumerable property (by default, properties created
// with Object.defineProperty are non-enumerable)
Object.defineProperty(obj, "four", {
value: 4,
configurable: true,
writable: true
});
var propNames = Object.keys(obj);
propNames.forEach(function(name) {
console.log(name + " = " + obj[name]);
});
With our sample setup, that gives us:
two = 2 three = 3
The order of the names in the array is not defined by the specification, not even in ES2015.
Object.getOwnPropertyNames
(ES5+)Object.getOwnPropertyNames
returns an array of the names of the object's own properties named with strings, whether enumerable or not. It leaves out properties named with Symbol
s.
var propNames = Object.getOwnPropertyNames(obj);
propNames.forEach(function(name) {
console.log(name + " = " + obj[name]);
});
// Create an object with one "own" property (plus the ones it
// inherits from Object.prototype):
var proto = {
one: 1
}
// Create an object that uses the above as its prototype
var obj = Object.create(proto);
// Add a couple of enumerable properties
obj.two = 2;
obj.three = 3;
// Add a non-enumerable property (by default, properties created
// with Object.defineProperty are non-enumerable)
Object.defineProperty(obj, "four", {
value: 4,
configurable: true,
writable: true
});
var propNames = Object.getOwnPropertyNames(obj);
propNames.forEach(function(name) {
console.log(name + " = " + obj[name]);
});
With our sample setup, that gives us:
two = 2 three = 3 four = 4
The order of the names in the array is defined by the [[OwnPropertyKeys]]
operation in the specification, which says the order is:
So we get two
, three
, four
because none of those fits the spec's definition of an integer index, and that's the order in which we added the properties. If we added them in a different order, or included ones that qualified as indexes, we'd get different results:
var obj2 = {};
obj2.four = 4;
obj2[0] = "zero";
obj2.two = 2;
obj2.three = 3;
Object.getOwnPropertyNames(obj2).forEach(function(name) {
console.log(name + " = " + obj2[name]);
});
var obj2 = {};
obj2.four = 4;
obj2[0] = "zero";
obj2.two = 2;
obj2.three = 3;
Object.getOwnPropertyNames(obj2).forEach(function(name) {
console.log(name + " = " + obj2[name]);
});
Gives us:
0 = zero four = 4 two = 2 three = 3
0
was first because although it's a string, it fits the criteria for an integer index. Then we get four
because it was created first, then two
, then three
.
Reflect.enumerate
(ES2015)Reflect.enumerate
was removed in ES2016.
Reflect.enumerate
uses the new iterator feature of ES2015. It returns an iterator that will iterate over the names of the string-named enumerable properties of the object, including inherited ones, skipping ones named with Symbol
or that aren't enumerable. First it visits the "own" properties (in the order defined by [[OwnPropertyKeys]]
, and then inherited properties (unless they've been hidden by "own" properties).
We can use the new for-of
loop to loop over them:
for (let name of Reflect.enumerate(obj)) {
console.log(name + " = " + obj[name]);
}
With our setup, that gives us:
two = 2 three = 3 one = 1
one
is at the end because it's inherited, and "own" properties come first.
Note: Again, since ES2015 is relatively new as of this writing, some JavaScript engines may not implement the Reflect
object yet.
Object.getOwnPropertySymbols
(ES2015+)Object.getOwnPropertySymbols
returns an array of an object's own properties named with Symbol
s, regardless of whether they're enumerable. (Leaves out ones named with strings.) We'll need a different setup to try this, since we didn't include Symbol
-named properties in our main setup. Since inherited properties are ignored, we'll keep it simple:
var obj3 = {};
obj3[Symbol("x")] = "ecks";
obj3[1] = "one";
obj3[Symbol("y")] = "why";
obj3.z = "zee";
Object.getOwnPropertySymbols(obj3).forEach(function(symbol) {
console.log(symbol.toString() + " = " + obj3[symbol]);
});
var obj3 = {};
obj3[Symbol("x")] = "ecks";
obj3[1] = "one";
obj3[Symbol("y")] = "why";
obj3.z = "zee";
Object.getOwnPropertySymbols(obj3).forEach(function(symbol) {
console.log(symbol.toString() + " = " + obj3[symbol]);
});
Output:
Symbol(x) = ecks Symbol(y) = why
z
wasn't listed because it has a string name, not a Symbol
name. Symbol(x)
was first because it was created first.
There's a lot more to Symbols
than shown here, but as you can see, if we give a symbol a name, we can use toString
to get back Symbol(the name here)
as a string. Interesting, we have to call toString
explicitly (thesymbol.toString()
) or use String(theSymbol
); the +
operator will not do it for us when appending a symbol to a string.
Reflect.ownKeys
(ES2015+)Reflect.ownKeys
returns an array of an object's own properties no matter how they're named (Symbol
or string), and whether they're enumerable or not. It ignores inherited properties:
var obj3 = {};
obj3[Symbol("x")] = "ecks";
obj3[1] = "one"
obj3[Symbol("y")] = "why";
obj3.z = "zee";
Reflect.ownKeys(obj3).forEach(function(key) {
console.log(key.toString() + " = " + obj3[key]);
});
var obj3 = {};
obj3[Symbol("x")] = "ecks";
obj3[1] = "one"
obj3[Symbol("y")] = "why";
obj3.z = "zee";
Reflect.ownKeys(obj3).forEach(function(key) {
console.log(key.toString() + " = " + obj3[key]);
});
Output:
1 = one z = zee Symbol(x) = ecks Symbol(y) = why
Note the order, which is defined by [[OwnPropertyKeys]]
: 1
was first because it's a string that qualifies as an integer index. z
was next because it's a string-named property. Then we have the Symbol
-named properties, in the order in which they were created.
Upvotes: 24
Reputation: 21
Inspired by above answers I wondered how to list ALL properties of any object (both emumerable and not) up the inheritance chain. This function seems sufficient.
var allPropNames = [];
function digAllPropsOut (ob){
var propNames = Object.getOwnPropertyNames(ob);
propNames.forEach(function(name) {
allPropNames.push(name);
});
// allPropNames.push('\n');
ob = Object.getPrototypeOf(ob);
if (ob)
digAllPropsOut(ob);
}
Upvotes: 1
Reputation: 31883
var data = { "key1":"value1", "key2":"value2"}; //etc
for (var prop in data) {
var propName = prop;
var propVal = data[prop];
// do something with your new variables
}
Yeah, it's that simple.
Upvotes: 6