以字符串形式获取对象属性名

是否可以将对象属性名称作为字符串获取

person = {};
person.first_name = 'Jack';
person.last_name = 'Trades';
person.address = {};
person.address.street = 'Factory 1';
person.address.country = 'USA';

我想这样使用它:

var pn = propName( person.address.country ); // should return 'country' or 'person.address.country'
var pn = propName( person.first_name );      // should return 'first_name' or 'person.first_name'

注意 : 这个代码正是我要找的。我知道这听起来很愚蠢,但它不是。

这就是我想用它做的事。

超文本标示语言

person = {};
person.id_first_name = 'Jack';
person.id_last_name = 'Trades';
person.address = {};
person.address.id_address = 'Factory 1';
person.address.id_country = 'USA';




extPort.postMessage
(
{
message : MSG_ACTION,
propName( person.first_name ): person.first_name
}
};

———————— 回答——————————

多亏了 ibu,他指出了正确的方向,我用了一个递归函数

var res = '';


function propName(prop, value) {
for (var i in prop) {
if (typeof prop[i] == 'object') {
if (propName(prop[i], value)) {
return res;
}
} else {
if (prop[i] == value) {
res = i;
return res;
}
}
}
return undefined;
}


var pn = propName(person, person.first_name); // returns 'first_name'
var pn = propName(person, person.address.country); // returns 'country'

演示: http://jsbin.com/iyabal/1/edit

136471 次浏览

Yes you can, with a little change.

function propName(prop, value){
for(var i in prop) {
if (prop[i] == value){
return i;
}
}
return false;
}

Now you can get the value like so:

 var pn = propName(person,person.first_name);
// pn = "first_name";

Note I am not sure what it can be used for.

Other Note wont work very well with nested objects. but then again, see the first note.

No, it's not possible.

Imagine this:

person.age = 42;
person.favoriteNumber = 42;


var pn = propName(person.age)
// == propName(42)
// == propName(person.favoriteNumber);

The reference to the property name is simply lost in that process.

You could create a namespacing method for the object. The method will need to mutate the object so that the strings becomes an object instead to hold two properties, a value and a _namespace.

DEMO: http://jsfiddle.net/y4Y8p/1/

var namespace = function(root, name) {
root._namespace = name;
function ns(obj) {
for( var i in obj ) {
var a = obj._namespace.split('.')
if ( a.length ) {
a.push(i);
}
if( typeof obj[i] == 'object' ) {
obj[i]._namespace = a.join('.');
ns(obj[i]);
return;
}
if( typeof obj[i] == 'string' ) {
var str = obj[i].toString();
obj[i] = {
_namespace: a.join('.'),
value: str
};
}
}
}
ns(root);
};


namespace(person, 'person');


console.log(person.address.street._namespace) // person.address.street
console.log(person.address.street.value) // 'Factory 1'

So now you can do:

var o = { message: MSG_ACTION };
o[ person.first_name._namespace ] = person.first_name.value;


extPort.postMessage(o);

You can wrap your property in a function and then convert the function to a string and get the property out of it.

For example:

function getPropertyName(propertyFunction) {
return /\.([^\.;]+);?\s*\}$/.exec(propertyFunction.toString())[1];
}

Then to use it:

var myObj = {
myProperty: "testing"
};


getPropertyName(function() { myObj.myProperty; }); // myProperty

Beware that minifiers could break this.

Edit: I have created a compiler transform that works with babel and the typescript compiler (see ts-nameof). This is a much more reliable than doing something at runtime.

I know a best practice that using Object.keys(your_object). It will parse to array property name for you. Example:

var person = { firstName: 'John', lastName: 'Cena', age: '30' };
var listPropertyNames = Object.keys(person); //["firstName", "lastName", "age"]

I hope this example is useful for you.

I am in same situation.

Here is thy way to get it done using Lodash or UnderScore library, with one limitation of value to be unique:

var myObject = {
'a': 1,
'b': 2,
'c': 3
}
_.findKey(myObject, function( curValue ) { return myObject.a === curValue });

Plain JavaScript

function getPropAsString( source, value ){
var keys = Object.keys( source );


var curIndex,
total,
foundKey;


for(curIndex = 0, total = keys.length; curIndex < total; curIndex++){
var curKey = keys[ curIndex ];
if ( source[ curKey ] === value ){
foundKey = curKey;
break;
}
}


return foundKey;
}


var myObject = {
'a': 1,
'b': 2,
'c': 3
}
getPropAsString( myObject, myObject.a )

But, I would prefer to fix the code as solution. An example:

var myObject = {
'a': {key:'a', value:1},
'b': {key:'b', value:2},
'c': {key:'c', value:3}
}


console.log( myObject.a.key )

You can accomplish this by converting all object properties into functions which will return the their own names

var person = {};
person.firstname = 'Jack';
person.address = "123 Street";


function getPropertyName(obj, expression) {
var res = {};
Object.keys(obj).map(k => { res[k] = () => k; });
return expression(res)();
}


let result = getPropertyName(person, o => o.address);
console.log(result); // Output: 'address'

I prefer it clean and simple like this:

var obj = {
sessionId: 123,
branchId: 456,
seasonId: 789
};


var keys = Object.keys(obj);


for (var i in keys) {
console.log(keys[i]); //output of keys as string
}

I am late to the party but I took a completely different approach, so I will throw in my approach and see what the community thinks.

I used Function.prototype.name to do what I want. my properties are functions that when called return the value of the property, and I can get the name of the property (which is a function) using .name

Here is an example:

person = {
firstName(){
return 'John';
},
address(){
return '123 street'
}
}


person.firstName.name // 'firstName'
person.address.name // 'address'

Note:
you can't easily change the value of a property (e.g firstname) at run time in this case. you would need to create a function (.name would be anonymous in this case) and this function would return a new named function which return the new value:

// note the () at the end
person.firstName = new Function('', 'return function firstName(){return "johny"}')();
person.firstName.name ; // 'firstName'
person.firstName(); // 'johny'

Following up on @David Sherret's answer with ES6 it can be made super simple:

propName = f => /\.([^\.;]+);?\s*\}$/.exec(f.toString())[1]
let prop = propName(() => {obj.name}); // myProperty

Using Proxy:

var propName = ( obj ) => new Proxy(obj, {
get(_, key) {
return key;
}
});




var person = {};
person.first_name = 'Jack';
person.last_name = 'Trades';
person.address = {};
person.address.street = 'Factory 1';
person.address.country = 'USA';


console.log(propName(person).first_name);
console.log(propName(person.address).country);

I like one liners, here's a generic solution:

const propName = (obj,type) => Object.keys(obj).find(key => obj[key] === type)


propName(person, person.age)

I use the following in TypeScript. This way retains type-information and disallows selecting non-existing property keys.

export function getPropertyName<T extends object>(obj: T, selector: (x: Record<keyof T, keyof T>) => keyof T): keyof T {
const keyRecord = Object.keys(obj).reduce((res, key) => {
const typedKey = key as keyof T
res[typedKey] = typedKey
return res
}, {} as Record<keyof T, keyof T>)
return selector(keyRecord)
}


const obj = {
name: 'test',
address: {
street: 'test',
}
}


console.log(getPropertyName(obj, (x) => x.name)) // name
console.log(getPropertyName(obj.address, (x) => x.street)) // street

If anyone's looking for a TypeScript version of MarsRobot's answer, try this:

function nameof<T>(obj: T, expression: (x: { [Property in keyof T]: () => string }) => () => string): string
{
const res: { [Property in keyof T]: () => string } = {} as { [Property in keyof T]: () => string };


Object.keys(obj).map(k => res[k as keyof T] = () => k);


return expression(res)();
}

Usage:

const obj = {
property1: 'Jim',
property2: 'Bloggs',
property3: 'Bloggs',
method: () => 'a string',
child: { property4: 'child1' }
};


const test1 = nameof(obj, x => x.property1);
const test2 = nameof(obj, x => x.property2);
const test3 = nameof(obj, x => x.method);
const test4 = nameof(obj.child, x => x.property4);


console.log(test1);    // -> 'property1'
console.log(test2);    // -> 'property2'
console.log(test3);    // -> 'method'
console.log(test4);    // -> 'property4'

This version works even when objects have multiple properties with the same value (unlike some of the other answers above), and with editors like Visual Studio will provide intellisense for the property names when you get here: nameof(obj, x => x.