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

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

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

有一个对象的两种访问属性的方法

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

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

var something = {bar: 'foo'};var foo = 'bar';
// both x = something[foo] and something[foo] = x work as expectedconsole.log(something[foo]);console.log(something.bar)

在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

更新中

使用obj[variable]很容易访问对象中的根属性,但嵌套会使事情复杂化。不要编写已经编写的代码,我建议使用lodash.get

示例

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

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

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

var suffix = " name";
var person = {["first" + suffix]: "Nicholas",["last" + suffix]: "Zakas"};
console.log(person["first name"]);      // "Nicholas"console.log(person["last name"]);       // "Zakas"

这叫做计算属性名称

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

代码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 begninningparameters[i] = parameters[i].replace(/^\s*['"]?/,'');// clean up param endparameters[i] = parameters[i].replace(/['"]?\s*$/,'');}
obj[func](parameters); // sends parameters as arrayobj[func].apply(this, parameters); // sends parameters as individual values

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

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'

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

这是我的解决方案:

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

使用示例:

resolve("document.body.style.width")// orresolve("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'})

你可以用Lodash get这样做

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

ES5//检查深度嵌套变量

这段简单的代码可以检查深度嵌套的变量/值是否存在,而无需检查每个变量。

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

Ex.-一个深度嵌套的对象数组:

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

干杯!

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

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

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

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

使用上面的代码示例;如果存储在'elem'变量中的div元素的宽度属性在CSS样式表中设置了样式,而不是在其超文本标记语言标记中设置了样式,那么毫无疑问,您将获得存储在cssProp变量中的未定义的返回值。发生未定义值是因为为了获得正确的值,CSS样式表中编写的代码需要按顺序计算才能获得值,因此;您必须使用一种方法来计算值位于样式表中的属性的值。

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

W3学校获取计算值文档这给出了一个很好的例子,让您可以使用它,但是,这个链接Mozilla CSS getComputedValue文档详细讨论了getComputedValue函数,任何对这个主题不完全清楚的有抱负的开发人员都应该阅读。

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

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

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

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

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

我希望能够传递一个数组,例如:[“audioPlayer”、“控件”、“停止”]来访问语言文本,在这种情况下是“停止”。

我创建了这个小函数,它查找“最不特定”(第一个)地址参数,并将返回的对象重新分配给自己。然后,如果存在,它就可以查找下一个最特定的地址参数。

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

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

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

通过引用查找对象,没有字符串,注意确保你传入的对象是克隆的,我使用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 = objectInfor (let i = 0; i <= path.length - 1; i++) {const item = path[i]// keep going up to the next parentobj = obj[item] // this is by reference}return obj}

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

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

问题

这个解决方案有一个很大的问题!(我很惊讶其他答案还没有提出这个问题)。通常你只想访问你自己放在那个对象上的属性,你不想抓住继承的属性。

这是这个问题的一个例子。这里我们有一个看似无辜的程序,但它有一个微妙的bug-你能发现它吗?

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是函数toString(){[本机代码]}岁”。问题是agesOfUsers是一个对象,因此会自动从基Object类继承某些属性,如.toString()。您可以在这里中查找所有对象继承的属性的完整列表。

解决方案

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

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 = 16agesOfUsers.sally = 22;console.log(agesOfUsers['sam']) // 16console.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. 如果您知道要使用的键永远不会是继承属性的名称(例如,它们可能是数字,或者它们都具有相同的前缀等),您可以选择使用原始解决方案。

您可以在JavaScript中使用getter

getter文档

检查对象内部有问题的属性是否存在,如果不存在,从窗户拿吧

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

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

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

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