【问题标题】:Get object property name as a string以字符串形式获取对象属性名称
【发布时间】:2012-11-16 17:07:18
【问题描述】:

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

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'

注意:这段代码正是我要找的。我知道这听起来很愚蠢,但事实并非如此。

这就是我想要做的。

HTML

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

【问题讨论】:

  • 我很困惑...为什么您希望属性名称返回您输入的相同内容?那么您已经知道属性名称...如果您正在寻找一种遍历属性的方法,您可以使用括号表示法并遍历键,因为属性也是哈希索引
  • 您还需要将对象的引用传递给函数。
  • 不是自动的。 country 属性引用的字符串对address 对象一无所知,address 属性引用的对象对person 对象一无所知。
  • 我知道这可能会造成混淆,但这正是我所需要的。我需要将属性名称作为字符串坚韧
  • -1 抱歉,您的代码确实是一个不好的做法。请参阅此示例:jsbin.com/iyabal/4/edit ... 和 en.wikipedia.org/wiki/Nestor_Burma 作为参考 ;-)

标签: javascript string object properties


【解决方案1】:

我知道使用Object.keys(your_object) 的最佳做法。它将为您解析为数组属性名称。示例:

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

希望这个例子对你有用。

【讨论】:

    【解决方案2】:

    您可以通过将所有对象属性转换为将返回它们自己的名称的函数来完成此操作

    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'
    

    【讨论】:

    • 这是最好的解决方案!
    • 如果您看到有关“无返回值”的警告;这是修改后的版本:Object.keys(obj).map(k => { res[k] = () => k; return k;});
    • 仅供参考,如果有人在寻找,我在下面添加了一个 TypeScript 版本。
    【解决方案3】:

    您可以将您的属性包装在一个函数中,然后将该函数转换为字符串并从中获取该属性。

    例如:

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

    然后使用它:

    var myObj = {
        myProperty: "testing"
    };
    
    getPropertyName(function() { myObj.myProperty; }); // myProperty
    

    注意缩小器可能会破坏这一点。

    编辑:我创建了一个编译器转换,可以与 babel 和 typescript 编译器一起使用(参见 ts-nameof)。这比在运行时做某事要可靠得多。

    【讨论】:

      【解决方案4】:

      我喜欢一个衬垫,这是一个通用的解决方案:

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

      【讨论】:

        【解决方案5】:

        是的,只需稍作改动即可。

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

        现在你可以像这样得到值:

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

        注意我不确定它可以用来做什么。

        其他注意事项不适用于嵌套对象。但话又说回来,请参阅第一个注释。

        【讨论】:

        • 这假定一个对象中没有两个属性具有相同的值。
        • 如果您通过 person.first_name,您的函数将返回“first_name”,但如果您使用 person.address.street,则返回 false。也许递归使用它?
        【解决方案6】:

        使用代理:

        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);

        【讨论】:

        • 为什么这个工作console.log(propName({}).first_name);
        • 当属性值相同时会失败。
        【解决方案7】:

        如果有人正在寻找 MarsRobot 答案的 TypeScript 版本,试试这个:

        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] = () => k);
        
            return expression(res)();
        }
        

        用法:

        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'
        

        即使对象具有多个具有相同值的属性(与上面的其他一些答案不同),此版本也可以工作,并且当您到达此处时,Visual Studio 等编辑器将为属性名称提供智能感知:nameof(obj, x =&gt; x.

        【讨论】:

          【解决方案8】:

          我在 TypeScript 中使用以下内容。这种方式保留了类型信息并且不允许选择不存在的属性键。

          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
          

          【讨论】:

          • 你有一个简单的例子如何使用这个方法吗?
          【解决方案9】:

          跟进@David Sherret 对 ES6 的回答,它可以变得超级简单:

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

          【讨论】:

            【解决方案10】:

            我更喜欢这样干净简单:

            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
            }
            

            【讨论】:

            • 你如何获得特定的密钥?
            • 为什么 Object.keys 应该返回一个特定的键?该问题的重点是,将对象的键作为字符串!?或不?我现在很困惑……
            • 是的,但是您遍历所有键。您的解决方案不可能获得特定的属性名称。
            • 啊,我明白了!不,我的解决方案只适用于所有键,你是对的。我误解了这个问题。
            【解决方案11】:

            不,这不可能。

            想象一下:

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

            对属性名称的引用在该过程中完全丢失。

            【讨论】:

              【解决方案12】:

              您可以为对象创建命名空间方法。该方法需要改变对象,以便字符串成为一个对象,而不是保存两个属性,一个value 和一个_namespace

              演示: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'
              

              所以现在你可以这样做了:

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

              【讨论】:

              • 我对这个答案很着迷,所以我开始研究它,试图找到一种更清洁的方法。这是我到目前为止的想法,你怎么看:jsfiddle.net/y4Y8p/17
              • @JasonSperske 很好,但它不是递归的,所以它只能工作 3 层深:jsfiddle.net/y4Y8p/18
              • jsfiddle.net/y4Y8p/22 立即尝试 :) 仍然存在更大的问题,例如它将所有值转换为字符串,但这是一段有趣的代码
              【解决方案13】:

              我也有同样的情况。

              这是您使用LodashUnderScore 库完成它的方法,其中一个限制是唯一的值:

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

              纯 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 )
              

              但是,我更愿意将代码修复为解决方案。一个例子:

              var myObject = {
              'a': {key:'a', value:1},
              'b': {key:'b', value:2},
              'c': {key:'c', value:3}
              }
              
              console.log( myObject.a.key )
              

              【讨论】:

                【解决方案14】:

                我迟到了,但我采取了完全不同的方法,所以我会提出我的方法,看看社区的想法。

                我使用Function.prototype.name 来做我想做的事。 我的属性是在调用时返回属性值的函数,我可以使用 .name 获取属性的名称(这是一个函数)

                这是一个例子:

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

                注意:
                在这种情况下,您无法在运行时轻松更改属性的值(例如名字)。 你需要创建一个函数(.name 在这种情况下是匿名的)并且这个函数将返回一个新的命名函数,该函数返回新值:

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

                【讨论】:

                • @Please_Dont_Bully_Me_SO_Lords,因此句子:“我的属性是调用时返回属性值的函数”
                • 这就是为什么我在回答中明确表示:1- 我采用不同的方法,2- 我的属性是函数,3- 我有一个关于如何更新 a 值的注释财产。我绝不是建议这是最好的方法,我只是提出一种方法来做 OP 想做的事情。在他的示例中,它是一个字符串,因为他使用的是普通属性,在我的方法中,我将它们包装在函数中。 OP 可以使用我的答案在他的属性周围添加包装器。我的属性在语法上是函数,是的。但是,从语义上讲,我的属性是函数调用的结果,
                • @Please_Dont_Bully_Me_SO_Lords,如果你看到这个答案可以改进,请成为我的客人。谢谢。
                猜你喜欢
                • 2011-09-30
                • 2015-05-27
                • 2015-03-18
                • 1970-01-01
                • 1970-01-01
                • 2019-12-07
                • 1970-01-01
                相关资源
                最近更新 更多