【问题标题】:Accessing an object property with a dynamically-computed name使用动态计算的名称访问对象属性
【发布时间】:2022-11-17 02:53:09
【问题描述】:

我正在尝试使用动态名称访问对象的属性。这可能吗?

const something = { bar: "Foobar!" };
const foo = 'bar';
something.foo; // The idea is to access something.bar, getting "Foobar!"

【问题讨论】:

标签: javascript object properties


【解决方案1】:

two ways to access properties的一个对象:

  • 点符号:something.bar
  • 括号符号:something['bar']

括号之间的值可以是任何表达式。因此,如果属性名称存储在变量中,则必须使用括号表示法:

var something = {
  bar: 'foo'
};
var foo = 'bar';

// both x = something[foo] and something[foo] = x work as expected
console.log(something[foo]);
console.log(something.bar)

【讨论】:

  • 小心这一点:javascript 编译器会在这里出错,因为它们不重命名字符串,但会重命名对象属性
  • 关于为什么这是可能的更多信息:JS 对象是关联数组,这就是原因。延伸阅读:quirksmode.org/js/associative.htmlstackoverflow.com/questions/14031368/…
  • @dotnetguy 不,他们不是。数组是从普通 JS 对象原型继承的对象,因此您可以像任何普通对象一样轻松添加属性。 “关联”行为比数组更像对象。您不能通过简单索引迭代“关联”版本,因此它不会显示类似数组的行为。您可以将“关联”数组定义为 {} 或 [],并且就随机属性访问而言,在任何一种情况下都将其视为相同。
  • @VanquishedWombat 不确定您的异议与什么有关?我没说JS对象是数组?
  • 以后请克制使用 foo-bar ……这个解释太含糊了。
【解决方案2】:

这是我的解决方案:

function resolve(path, obj) {
    return path.split('.').reduce(function(prev, curr) {
        return prev ? prev[curr] : null
    }, obj || self)
}

使用示例:

resolve("document.body.style.width")
// or
resolve("style.width", document.body)
// or even use array indexes
// (someObject has been defined in the question)
resolve("part.0.size", someObject) 
// returns null when intermediate properties are not defined:
resolve('properties.that.do.not.exist', {hello:'world'})

【讨论】:

  • 优秀的答案,另见:stackoverflow.com/questions/37510640/…
  • 你启发我创建了一个增强版本,它允许括号符号和带有空格的属性名称以及验证输入:it.knightnet.org.uk/kb/node-js/get-properties
  • 我喜欢这个解决方案。但是我试图修改原始对象中的值,看起来你的函数返回了对象的子副本。是否可以更改它以便修改返回的对象会修改原始对象?
  • 我还想看看这个的“设定值”版本。
  • 很好的答案!它适用于深层嵌套的属性。
【解决方案3】:

在javascript中我们可以访问:

  • 点符号 - foo.bar
  • 方括号 - foo[someVar]foo["string"]

但只有第二种情况允许动态访问属性:

var foo = { pName1 : 1, pName2 : [1, {foo : bar }, 3] , ...}

var name = "pName"
var num  = 1;

foo[name + num]; // 1

// -- 

var a = 2;
var b = 1;
var c = "foo";

foo[name + a][b][c]; // bar

【讨论】:

  • 我盯着 2,000 行 if 语句,因为以前的开发人员没有使用方括号,而是通过点符号静态访问对象属性。它适用于具有 7 个不同审批人且步骤完全相同的审批流程应用程序。 /撕裂
【解决方案4】:

以下是一个 ES6 示例,说明如何使用通过连接两个字符串动态生成的属性名称来访问对象的属性。

var suffix = " name";

var person = {
    ["first" + suffix]: "Nicholas",
    ["last" + suffix]: "Zakas"
};

console.log(person["first name"]);      // "Nicholas"
console.log(person["last name"]);       // "Zakas"

这个叫computed property names

【讨论】:

    【解决方案5】:

    您可以通过多种不同的方式实现这一目标。

    let foo = {
        bar: 'Hello World'
    };
    
    foo.bar;
    foo['bar'];
    

    括号表示法特别强大,因为它让您可以访问基于变量的属性:

    let foo = {
        bar: 'Hello World'
    };
    
    let prop = 'bar';
    
    foo[prop];
    

    这可以扩展到遍历对象的每个属性。由于较新的 JavaScript 构造(例如 for ... of ...),这似乎是多余的,但有助于说明用例:

    let foo = {
        bar: 'Hello World',
        baz: 'How are you doing?',
        last: 'Quite alright'
    };
    
    for (let prop in foo.getOwnPropertyNames()) {
        console.log(foo[prop]);
    }
    

    对于嵌套对象,点和括号表示法也可以按预期工作:

    let foo = {
        bar: {
            baz: 'Hello World'
        }
    };
    
    foo.bar.baz;
    foo['bar']['baz'];
    foo.bar['baz'];
    foo['bar'].baz;
    

    对象解构

    我们还可以将对象解构视为访问对象中的属性的一种方式,但如下所示:

    let foo = {
        bar: 'Hello World',
        baz: 'How are you doing?',
        last: 'Quite alright'
    };
    
    let prop = 'last';
    let { bar, baz, [prop]: customName } = foo;
    
    // bar = 'Hello World'
    // baz = 'How are you doing?'
    // customName = 'Quite alright'
    

    【讨论】:

      【解决方案6】:

      您可以使用 Lodash get 这样做

      _.get(object, 'a[0].b.c');
      

      【讨论】:

      • 在许多情况下,例如深层嵌套对象查找,这是唯一的选择。
      • jQuery 不够用。
      【解决方案7】:

      更新

      使用 obj[variable] 可以轻松访问对象中的根属性,但是嵌套会使事情变得复杂。不要写已经写好的代码我建议使用lodash.get

      例子

      // Accessing root property
      var rootProp = 'rootPropert';
      _.get(object, rootProp, defaultValue);
      
      // Accessing nested property
      var listOfNestedProperties = [var1, var2];
      _.get(object, listOfNestedProperties);
      

      Lodash get 可以有不同的使用方式,文档lodash.get

      【讨论】:

      • 最好尽可能避免使用evalstackoverflow.com/questions/86513/…
      • eval 用于像访问属性这样微不足道的事情显然是矫枉过正,在任何情况下都不可取。什么是“麻烦”? obj['nested']['test'] 工作得很好,不需要你在字符串中嵌入代码。
      • eval 慢三倍或更多,我不会向新手推荐这个,因为它可能会教给他们坏习惯。我使用 obj['nested']['value'] - 记住孩子们,eval 是邪恶的!
      • @Luke 他现在是唯一想将 Lodash _.get 带到桌面上的人。我认为这个答案现在值得赞成而不是反对。这可能有点矫枉过正,但很高兴知道它的存在。
      • 感谢您为此引入 lodash。我通过谷歌来到这里寻找一种在对象深处设置值的方法,并使用了他们的 _.set 方法(与上面相同,但对要设置的值有额外的争论)。
      【解决方案8】:

      我遇到了一个案例我想我想将对象属性的“地址”作为数据传递给另一个函数并填充对象(使用 AJAX),从地址数组中进行查找,然后在另一个函数中显示。如果不做字符串杂技,我就不能使用点表示法,所以我认为传递数组可能会更好。无论如何,我最终做了一些不同的事情,但似乎与这篇文章有关。

      这是一个语言文件对象的示例,就像我想要从中获取数据的对象一样:

      const locs = {
        "audioPlayer": {
          "controls": {
            "start": "start",
            "stop": "stop"
          },
          "heading": "Use controls to start and stop audio."
        }
      }
      

      我希望能够传递一个数组,例如:["audioPlayer", "controls", "stop"] 以访问语言文本,在本例中为“stop”。

      我创建了这个查找“最不具体”(第一个)地址参数并将返回的对象重新分配给自身的小函数。然后准备好查找下一个最具体的地址参数(如果存在的话)。

      function getText(selectionArray, obj) {
        selectionArray.forEach(key => {
          obj = obj[key];
        });
        return obj;
      }
      

      用法:

      /* returns 'stop' */
      console.log(getText(["audioPlayer", "controls", "stop"], locs)); 
      
      /* returns 'use controls to start and stop audio.' */
      console.log(getText(["audioPlayer", "heading"], locs)); 
      

      【讨论】:

        【解决方案9】:

        要动态访问属性,只需使用square brackets[],如下所示:

        const something = { bar: "Foobar!" };
        const userInput = 'bar';
        console.log(something[userInput])

        问题

        该解决方案中有一个主要问题! (我很惊讶其他答案还没有提出来)。通常,您只想访问您自己赋予该对象的属性,而不想获取继承的属性。

        这是这个问题的一个例子。这里我们有一个看起来无害的程序,但它有一个微妙的错误 - 你能发现它吗?

        const agesOfUsers = { sam: 16, sally: 22 }
        const username = prompt('Enter a username:')
        if (agesOfUsers[username] !== undefined) {
          console.log(`${username} is ${agesOfUsers[username]} years old`)
        } else {
          console.log(`${username} is not found`)
        }

        当提示输入用户名时,如果您提供“toString”作为用户名,它会给您以下消息:“toString is function toString() { [native code] } years old”。问题是 agesOfUsers 是一个对象,因此会自动从基类 Object 继承某些属性,如 .toString()。您可以查看here 以获得所有对象继承的属性的完整列表。

        解决方案

        1. 改用Map data structure。地图的存储内容不会受到原型问题的影响,因此它们为这个问题提供了一个干净的解决方案。

          const agesOfUsers = new Map()
          agesOfUsers.set('sam', 16)
          agesOfUsers.set('sally', 2)
          console.log(agesOfUsers.get('sam')) // 16
          
            
          1. 使用具有空原型的对象,而不是默认原型。您可以使用Object.create(null) 来创建这样的对象。这种对象不会受到这些原型问题的困扰,因为您已经明确地以不继承任何东西的方式创建了它。

          const agesOfUsers = Object.create(null)
          agesOfUsers.sam = 16
          agesOfUsers.sally = 22;
          console.log(agesOfUsers['sam']) // 16
          console.log(agesOfUsers['toString']) // undefined - toString was not inherited
          
            
          1. 您可以使用Object.hasOwn(yourObj, attrName)首先检查您要访问的动态密钥是否直接在对象上而不是继承(了解更多here)。这是一个相对较新的功能,因此在将其放入您的代码之前检查兼容性表。在 Object.hasOwn(yourObj, attrName) 出现之前,您可以通过 Object.prototype.hasOwnProperty.call(yourObj, attrName) 实现同样的效果。有时,您可能也会看到使用 yourObj.hasOwnProperty(attrName) 的代码,这有时可以工作,但它有一些陷阱,您可以阅读有关 here 的内容。

          // Try entering the property name "toString",
          // you'll see it gets handled correctly.
          const user = { name: 'sam', age: 16 }
          const propName = prompt('Enter a property name:')
          if (Object.hasOwn(user, propName)) {
            console.log(`${propName} = ${user[propName]}`)
          } else {
            console.log(`${propName} is not found`)
          }
          1. 如果您知道您尝试使用的密钥永远不会是继承属性的名称(例如,它们可能是数字,或者它们都具有相同的前缀等),您可以选择使用原始解决方案.

        【讨论】:

          【解决方案10】:

          ES5 // 检查深度嵌套的变量

          这段简单的代码可以检查深层嵌套的变量/值是否存在,而不必沿途检查每个变量......

          var getValue = function( s, context ){
              return Function.call( context || null, 'return ' + s )();
          }
          

          前任。 - 一个深度嵌套的对象数组:

          a = [ 
              {
                b : [
                    {
                       a : 1,
                       b : [
                           {
                              c : 1,
                              d : 2   // we want to check for this
                           }
                       ]
                     }
                ]
              } 
          ]
          

          代替 :

          if(a && a[0] && a[0].b && a[0].b[0] && a[0].b[0].b && a[0].b[0].b[0] && a[0].b[0].b[0].d && a[0].b[0].b[0].d == 2 )  // true
          

          我们现在可以:

          if( getValue('a[0].b[0].b[0].d') == 2 ) // true
          

          干杯!

          【讨论】:

          • 如果解决方案是使用 eval,那么您只会产生一百万个其他问题。
          • @RodrigoLeite 好的,所以至少给一个不是问题......
          • @RodrigoLeite 我已阅读,并更新了解决方案以使用Function 代替
          • 在 ECMAScript 2020 中,正确的做法是使用 optional chainingif(a?.[0]?.b?.[0]?.b?.[0]?.d === 2){}
          【解决方案11】:

          其他人已经提到了“点”和“方”语法,所以我想以类似的方式介绍访问函数和发送参数。

          代码 jsfiddle

          var obj = {method:function(p1,p2,p3){console.log("method:",arguments)}}
          
          var str = "method('p1', 'p2', 'p3');"
          
          var match = str.match(/^s*(S+)((.*));s*$/);
          
          var func = match[1]
          var parameters = match[2].split(',');
          for(var i = 0; i < parameters.length; ++i) {
            // clean up param begninning
              parameters[i] = parameters[i].replace(/^s*['"]?/,'');
            // clean up param end
            parameters[i] = parameters[i].replace(/['"]?s*$/,'');
          }
          
          obj[func](parameters); // sends parameters as array
          obj[func].apply(this, parameters); // sends parameters as individual values
          

          【讨论】:

            【解决方案12】:

            不久前我问了一个关于这个主题有点重复的问题,经过大量研究,看到这里应该有很多信息丢失了,我觉得我有一些有价值的东西可以添加到这篇旧帖子中。

            • 首先,我想说明有几种方法可以获取属性值并将其存储在动态变量中。恕我直言,第一个最受欢迎和最简单的方法是:
            let properyValue = element.style['enter-a-property'];
            

            但是我很少走这条路,因为它不适用于通过样式表分配的属性值。举个例子,我将用一些伪代码来演示。

             let elem = document.getElementById('someDiv');
             let cssProp = elem.style['width'];
            
            

            使用上面的代码示例;如果存储在“elem”变量中的 div 元素的宽度属性在 CSS 样式表中设置了样式,而不是在其 HTML 标记内设置了样式,那么毫无疑问,您将获得存储在其中的未定义返回值cssProp 变量。出现未定义值是因为为了获得正确的值,需要计算编写在 CSS 样式表中的代码以便获得该值,因此;您必须使用一种方法来计算样式表中属性的值。

            • 以后是 getComputedStyle() 方法!
            function getCssProp(){
              let ele = document.getElementById("test");
              let cssProp = window.getComputedStyle(ele,null).getPropertyValue("width");
            }
            
            

            W3Schools getComputedValue Doc 这是一个很好的示例,您可以尝试使用它,但是,此链接 Mozilla CSS getComputedValue doc 详细讨论了 getComputedValue 函数,任何对此主题不完全清楚的有抱负的开发人员都应该阅读。

            • 作为旁注,getComputedValue 方法只能获取,不能设置。这显然是一个主要缺点,但是有一种方法可以从 CSS 样式表获取并设置值,尽管它不是标准的 Javascript。 JQuery 方法...
            $(selector).css(property,value)
            

            ...确实得到了,也确实得到了。这是我使用的,唯一的缺点是你必须了解 JQuery,但老实说,这是每个 Javascript 开发人员都应该学习 JQuery 的众多好理由之一,它只是让生活变得轻松,并提供方法,比如这个,不适用于标准 Javascript。 希望这对某人有帮助!!!

            【讨论】:

            【解决方案13】:

            对于任何想要设置嵌套变量值的人,这里是如何做的:

            const _ = require('lodash'); //import lodash module
            
            var object = { 'a': [{ 'b': { 'c': 3 } }] };
            
            _.set(object, 'a[0].b.c', 4);
            console.log(object.a[0].b.c);
            // => 4
            

            文档:https://lodash.com/docs/4.17.15#set

            另外,如果您想获得一个值,请提供文档:https://lodash.com/docs/4.17.15#get

            【讨论】:

              【解决方案14】:

              您可以使用括号表示法动态访问对象的属性。这看起来像这样obj[yourKey]然而 JavaScript 对象实际上并不是设计用于动态更新或读取的.他们的目的是在初始化时定义.

              如果您想动态分配和访问 key value 对,您应该改用地图。

              const yourKey = 'yourKey';
              
              // initialise it with the value
              const map1 = new Map([
                ['yourKey', 'yourValue']
              ]);
              
              // initialise empty then dynamically assign
              const map2 = new Map();
              map2.set(yourKey, 'yourValue');
              
              console.log(map1.get(yourKey));
              console.log(map2.get(yourKey));

              【讨论】:

                【解决方案15】:

                通过引用查找对象,不带字符串, 注意确保你传入的对象是克隆的,我使用 lodash 的 cloneDeep 来实现

                如果对象看起来像

                const obj = {data: ['an Object',{person: {name: {first:'nick', last:'gray'} }]
                

                路径看起来像

                const objectPath = ['data',1,'person',name','last']
                

                然后调用下面的方法,它将通过给定的路径返回子对象

                const child = findObjectByPath(obj, objectPath)
                alert( child) // alerts "last"
                
                
                const findObjectByPath = (objectIn: any, path: any[]) => {
                    let obj = objectIn
                    for (let i = 0; i <= path.length - 1; i++) {
                        const item = path[i]
                        // keep going up to the next parent
                        obj = obj[item] // this is by reference
                    }
                    return obj
                }
                

                【讨论】:

                  【解决方案16】:

                  您可以在 Javascript 中使用 getter

                  getter Docs

                  检查对象内部是否存在相关属性, 如果它不存在,从窗口拿走

                  const something = {
                      get: (n) => this.n || something.n || window[n]
                  };
                  

                  【讨论】:

                    【解决方案17】:

                    你应该使用JSON.parse,看看https://www.w3schools.com/js/js_json_parse.asp

                    const obj = JSON.parse('{ "name":"John", "age":30, "city":"New York"}')
                    console.log(obj.name)
                    console.log(obj.age)
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2012-01-20
                      • 1970-01-01
                      • 2010-12-20
                      相关资源
                      最近更新 更多