使用变量动态访问对象属性

Posted

技术标签:

【中文标题】使用变量动态访问对象属性【英文标题】:Dynamically access object property using variable 【发布时间】:2021-12-05 10:49:32 【问题描述】:

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

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

【问题讨论】:

另见property access: dot notation vs. brackets?和How do I add a property to an object using a variable as the name? 【参考方案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.html***.com/questions/14031368/… @dotnetguy 不,他们不是。数组是从普通 JS 对象原型继承的对象,因此您可以像任何普通对象一样添加属性。 “关联”行为更像对象而不是数组。您不能通过简单索引迭代“关联”版本,因此它不会显示类似数组的行为。您可以将“关联”数组定义为 或 [],并在任何一种情况下都将其视为随机属性访问。 @VanquiishedWombat 不确定您的反对意见与什么有关?我不是说 JS Objects 是数组吗? 以后请不要再使用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')

【讨论】:

优秀答案,另见:***.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];

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

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

【讨论】:

尽可能避免使用eval。 ***.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】:

const objA = 
    key1: "value 1",
    key2: "value 2",



//objA.key1
//objA[‘key1’]
//const s = “key1”; 

console.log(objA['key1']);

我们可以通过在文字对象中定义变量来在 Javascript 中创建属性。我们可以通过多种方式访问​​我们的对象属性,例如:

know more

【讨论】:

【参考方案10】:

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

代码 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

【讨论】:

【参考方案11】:

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 好的,所以至少提供一个... 给你 :) developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…! @RodrigoLeite 我已阅读并更新了解决方案以改用Function ECMAScript 2020 中的正确方法是使用 optional chaining: if(a?.[0]?.b?.[0]?.b?.[0]?.d === 2) ... 【参考方案12】:

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

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

【讨论】:

【参考方案13】:

要动态访问属性,只需使用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 以获取所有对象继承的属性的完整列表。

解决方案

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

const agesOfUsers = new Map()
agesOfUsers.set('sam', 16)
agesOfUsers.set('sally', 2)
console.log(agesOfUsers.get('sam')) // 16

  
    使用具有空原型的对象,而不是默认原型。您可以使用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

  
    如果您处理的对象的不同可能键是事先已知的(或者,至少已知它不包含与继承键冲突的键,例如数字键),那么您可以使用 Object.hasOwn(yourObj, attrName) 来代替检查您正在访问的特定动态密钥是否实际上是继承的密钥(了解更多信息here)。在撰写本文时,这是目前浏览器刚刚开始实施的 Javascript 的第 3 阶段提案,这意味着为了获得最佳稳定性,您需要使用 polyfill 或使用较旧、更冗长但完全相同的解决方案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.prototype.hasOwnProperty.call(user, propName)) 
  console.log(`$propName = $user[propName]`)
 else 
  console.log(`$propName is not found`)

【讨论】:

【参考方案14】:

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

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

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

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

使用上面的代码示例;如果存储在 'elem' 变量中的 div 元素的 width 属性在 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。 希望这对某人有帮助!!!

【讨论】:

【参考方案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】:

您可以使用动态变量来解构对象。在 Destructuring assignment 的 MDN 文档中,您会在副标题下找到一个类似的示例,如下所示:'Computed object property names and destructuring'。

const something =  bar: "Foobar!" ;
let key = 'bar';
let [key]: foo = something;
console.log(foo);

【讨论】:

【参考方案18】:

你应该使用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)

【讨论】:

以上是关于使用变量动态访问对象属性的主要内容,如果未能解决你的问题,请参考以下文章

使用变量动态访问对象属性

使用变量动态访问对象属性

17类的自定义属性访问及动态属性设置

无法从动态导入访问对象属性。 Vue.js + ts

js访问对象属性的方式“.”与“[]”的区别

在 C# 4 中使用动态类型访问 javascript 对象的属性