使用动态计算的名称访问对象属性

Accessing an object property with a dynamically-computed name

提问人:RichW 提问时间:11/22/2010 最后编辑:user3840170RichW 更新时间:10/10/2023 访问量:639231

问:

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

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

答:

1288赞 Jan Hančič 11/22/2010 #1

有两种方法可以访问对象的属性:

  • 点符号: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)

评论

47赞 chacham15 12/6/2011
注意这一点:JavaScript 编译器在这里会出错,因为它们不会重命名字符串,但它们会重命名对象属性
9赞 Sudhanshu Mishra 6/3/2014
关于为什么这是可能的更多信息:JS 对象是关联数组,这就是原因。延伸阅读:quirksmode.org/js/associative.html stackoverflow.com/questions/14031368/...
2赞 Vanquished Wombat 1/4/2017
@dotnetguy 不,他们不是。数组是从普通 JS 对象原型继承的对象,因此您可以像任何普通对象一样随意添加属性。“关联”行为更像是对象,而不是数组。您不能通过简单的索引来迭代“关联”版本,因此它不会显示类似数组的行为。您可以将“关联”数组定义为 {} 或 [],并在任何一种情况下都将其视为随机属性访问。
3赞 Sudhanshu Mishra 1/6/2017
@VanquishedWombat 不确定您的反对意见与什么有关?我不是说JS对象是数组吗?
2赞 Cornelius 1/25/2022
以后请不要使用foo-bar...这种解释是如此令人困惑。
64赞 Sonique 7/1/2014 #2

在 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

评论

13赞 Chad 6/7/2018
我盯着 2,000 行 if 语句,因为以前的开发人员没有使用方括号,并且通过点表示法静态访问对象属性。它适用于具有 7 个不同审批者的审批流程应用,步骤都相同。/把
15赞 Mr Br 6/22/2015 #3

更新

使用 很容易访问对象中的根属性,但嵌套会使事情变得复杂。不要写已经写好的代码,我建议使用.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

评论

5赞 Luke 6/24/2015
最好尽可能避免使用。stackoverflow.com/questions/86513/......eval
9赞 Kyll 10/23/2015
用于像访问属性这样微不足道的事情是矫枉过正,在任何情况下都不可取。什么是“麻烦”? 工作得很好,不需要你在字符串中嵌入代码。evalobj['nested']['test']
4赞 jaggedsoft 11/26/2015
eval 的速度慢了三倍或更多,我不建议新手这样做,因为它可能会教他们坏习惯。我用 - 记住孩子们,eval 是邪恶的!obj['nested']['value']
2赞 Emile Bergeron 12/21/2016
@Luke 他现在是唯一想把洛达什带到谈判桌上的人。我认为这个答案现在应该得到赞成票而不是反对票。这可能有点矫枉过正,但知道它的存在是件好事。_.get
2赞 TPHughes 7/3/2018
感谢您为此介绍 lodash。我通过谷歌来到这里寻找一种在对象深处设置值的方法,并使用了他们的 _.set 方法(与上面相同,但带有要设置的值的额外参数)。
41赞 zloctb 8/3/2016 #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"

这称为计算属性名称

3赞 Jacksonkr 11/14/2016 #5

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

代码 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
27赞 Gorka Hernandez 3/8/2017 #6

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

let foo = {
    bar: 'Hello World'
};

foo.bar;
foo['bar'];

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

let foo = {
    bar: 'Hello World'
};

let prop = 'bar';

foo[prop];

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

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'
-4赞 onmyway133 6/15/2017 #7

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

const obj = JSON.parse('{ "name":"John", "age":30, "city":"New York"}')
console.log(obj.name)
console.log(obj.age)
194赞 abahet 7/26/2017 #8

这是我的解决方案:

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'})

评论

4赞 Julian Knight 1/3/2019
优秀的答案,另见:stackoverflow.com/questions/37510640/...
7赞 Julian Knight 1/3/2019
你启发我创建一个增强版本,允许括号符号和属性名称与空格以及验证输入:it.knightnet.org.uk/kb/node-js/get-properties
3赞 Eagle1 2/29/2020
我喜欢这个解决方案。但是我正在尝试修改原始对象中的值,似乎您的函数返回了该对象的子副本。是否可以更改它,以便修改返回的对象修改原始对象?
3赞 GaryO 8/22/2020
我还想看看这个的“设定值”版本。
0赞 Scotty Jamison 12/3/2022
请注意,现代 JS 引擎现在支持 ,允许您实现与此相同的效果: .无需辅助功能。更多信息?.document.body?.style?.width
18赞 shalonteoh 9/6/2018 #9

你可以像这样使用 Lodash get 来做到这一点

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

评论

0赞 Jonathan Kempf 6/28/2019
在许多情况下,例如深度嵌套对象查找,这是唯一的选择。
0赞 user3840170 4/7/2021
没有足够的jQuery。
3赞 user4602228 8/21/2019 #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

干杯!

评论

1赞 Rodrigo Leite 12/9/2019
如果解决方案是使用 eval,那么您只是创建了一百万个其他问题。
0赞 12/9/2019
@RodrigoLeite好吧,所以至少给一个不是问题......
0赞 Rodrigo Leite 12/10/2019
在这里,你去:)developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
1赞 12/10/2019
@RodrigoLeite我已经阅读了它,并更新了解决方案以代替它Function
2赞 Sebastian Simon 7/1/2020
在 ECMAScript 2020 中,正确的方法是使用可选的链接:... .if(a?.[0]?.b?.[0]?.b?.[0]?.d === 2){}
0赞 JΛYDΞV 1/17/2020 #11

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

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

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

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

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

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

W3Schools getComputedValue 文档这提供了一个很好的例子,并让你使用它,但是,这个链接Mozilla CSS getComputedValue doc详细讨论了getComputedValue函数,任何有抱负的开发人员都应该阅读这个主题。

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

...确实得到,并且确实设置。这是我使用的,唯一的缺点是你必须了解 JQuery,但老实说,这是每个 Javascript 开发人员都应该学习 JQuery 的众多充分理由之一,它只是让生活变得简单,并提供像这样的方法,这是标准 Javascript 所没有的。 希望这对某人有所帮助!!

评论

0赞 Heretic Monkey 10/26/2022
这里的内容是许多其他问题及其答案的重复(例如,JavaScript get Styles,myDiv.style.display 在主样式表中设置时返回空白)应该引用这些问题和答案,而不是另一个答案说同样的事情正在创建。
6赞 Luke 2/26/2020 #12

我遇到过这样一种情况,我认为我想将对象属性的“地址”作为数据传递给另一个函数并填充该对象(使用 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)); 
0赞 GavinBelson 5/21/2020 #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

-1赞 nick 8/28/2020 #14

通过引用查找对象,不带字符串、 注意确保您传入的对象是克隆的,我使用 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
}
11赞 Scotty Jamison 7/14/2021 #15

若要动态访问属性,只需使用方括号,如下所示:[]

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”。问题在于它是一个对象,因此,它会自动继承某些属性,例如从基 Object 类继承。您可以在此处查找所有对象继承的属性的完整列表。agesOfUsers.toString()

解决 方案

  1. 请改用 Map 数据结构。地图的存储内容不会受到原型问题的影响,因此它们为此问题提供了干净的解决方案。

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

  

  1. 使用具有 null 原型的对象,而不是默认原型。您可以使用来创建此类对象。这种对象不会受到这些原型问题的影响,因为您已经以一种不继承任何内容的方式显式创建了它。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)Object.hasOwn(yourObj, attrName)Object.prototype.hasOwnProperty.call(yourObj, attrName)yourObj.hasOwnProperty(attrName)

// 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. 如果您知道您尝试使用的密钥永远不会是继承属性的名称(例如,它们可能是数字,或者它们都具有相同的前缀等),则可以选择使用原始解决方案。
-1赞 Ali Yaghoubi 10/14/2021 #16

您可以在 Javascript 中使用getter

getter 文档

检查对象内部是否存在相关属性, 如果它不存在,请从窗口中取出它

const something = {
    get: (n) => this.n || something.n || window[n]
};
0赞 Vincent Menzel 8/30/2022 #17

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

如果要动态分配和访问对,则应改用映射。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));

1赞 Jocefyneroot 12/9/2022 #18

演示对象示例

let obj = {
    name: {
        first_name: "Bugs",
        last_name: "Founder",
        role: "Programmer"
    }
}

虚线字符串键,用于获取

let key = "name.first_name"

功能

const getValueByDottedKeys = (obj, strKey)=>{
    let keys = strKey.split(".")
    let value = obj[keys[0]];   
    for(let i=1;i<keys.length;i++){
        value = value[keys[i]]
    }
    return value
}

调用 getValueByDottedKeys 函数

value = getValueByDottedKeys(obj, key)
console.log(value)

输出

Bugs

const getValueByDottedKeys = (obj, strKey)=>{
    let keys = strKey.split(".")
    let value = obj[keys[0]];   
    for(let i=1;i<keys.length;i++){
        value = value[keys[i]]
    }
    return value
}

let obj = {
    name: {
        first_name: "Bugs",
        last_name: "Founder",
        role: "Programmer"
    }
}

let key = "name.first_name"

value = getValueByDottedKeys(obj, key)
console.log(value)

0赞 Jorge M. Londoño P. 1/11/2023 #19

我遇到了同样的问题,但是 lodash 模块在处理嵌套属性时受到限制。我按照递归后代解析器的想法编写了一个更通用的解决方案。此解决方案在以下要点中可用:

递归下降对象取消引用

0赞 Surya R Praveen 10/10/2023 #20

这里是动态访问对象属性的一种方法,案例是基于 localstorage 数据访问 JSON 对象 {preferredIndustry= foodItemsFeatured}

data.foodItems要访问的精选内容

enter image description here

let storageData = this.data; // JSON Response
let preferredData = localStorage.getItem('preferredIndustry'); //foodItemsFeatured

/* Dynamically access Object's property */
this.preferredFeaturedItems = this.data[preferredData as keyof typeof storageData];