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

Ric*_*chW 654 javascript properties object

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

const something = { bar: "Foobar!" };
const foo = 'bar';
something.foo; // The idea is to access something.bar, getting "Foobar!"
Run Code Online (Sandbox Code Playgroud)

Jan*_*čič 866

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

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

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

var foo = 'bar';
something[foo];
// both x = something[foo] and something[foo] = x work as expected
Run Code Online (Sandbox Code Playgroud)

  • 小心这个:javascript编译器会在这里出错,因为它们不重命名字符串,但它们会重命名对象属性 (31认同)
  • 关于为什么这是可能的更多信息:JS对象是关联数组,这就是原因.进一步阅读:http://www.quirksmode.org/js/associative.html http://stackoverflow.com/questions/14031368/is-javascript-object-nothing-but-an-associative-array (6认同)
  • @VanquishedWombat不确定您的反对意见是什么?我没有说JS Objects是数组? (3认同)
  • @dotnetguy 不,他们不是。数组是从普通 JS 对象原型继承的对象,因此您可以像任何普通对象一样添加属性。“关联”行为比数组更像对象。您无法通过简单索引迭代“关联”版本,因此它不会显示类似数组的行为。您可以将“关联”数组定义为 {} 或 [],并在随机属性访问方面将其视为相同的情况。 (2认同)
  • 请以后不要使用 foo-bar...这个解释太混乱了。 (2认同)

aba*_*het 76

这是我的解决方案:

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

用法示例:

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'})
Run Code Online (Sandbox Code Playgroud)

  • 您启发我创建了一个增强版本,允许括号符号和带有空格的属性名称以及验证输入:https://it.knightnet.org.uk/kb/node-js/get-properties/ (3认同)
  • 我喜欢这个解决方案。不过,我正在尝试修改原始对象中的值,看来您的函数返回了该对象的子副本。是否可以更改它以便修改返回的对象会修改原始对象? (3认同)
  • 我还想看看这个的“设定值”版本。 (3认同)
  • 优秀的答案,另见:/sf/ask/2625744831/ (2认同)

Son*_*que 33

在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
Run Code Online (Sandbox Code Playgroud)

  • 我盯着2000行的if语句,因为以前的开发人员没有使用方括号,而是通过点表示法来静态访问对象属性。它适用于具有7个不同批准者的批准流程应用程序,步骤均相同。/ rip (2认同)

zlo*_*ctb 23

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

var suffix = " name";

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

console.log(person["first name"]);      // "Nicholas"
console.log(person["last name"]);       // "Zakas"
Run Code Online (Sandbox Code Playgroud)

这称为计算属性名称


Gor*_*dez 15

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

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

foo.bar;
foo['bar'];
Run Code Online (Sandbox Code Playgroud)

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

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

let prop = 'bar';

foo[prop];
Run Code Online (Sandbox Code Playgroud)

这可以扩展为循环对象的每个属性.由于更新的JavaScript构造(例如......),这似乎是多余的,但有助于说明一个用例:

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

for (let prop in foo.getOwnPropertyNames()) {
    console.log(foo[prop]);
}
Run Code Online (Sandbox Code Playgroud)

点和括号表示法也适用于嵌套对象:

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

foo.bar.baz;
foo['bar']['baz'];
foo.bar['baz'];
foo['bar'].baz;
Run Code Online (Sandbox Code Playgroud)

对象解构

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

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'
Run Code Online (Sandbox Code Playgroud)


Sco*_*son 11

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

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

问题

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

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

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`)
}
Run Code Online (Sandbox Code Playgroud)

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

解决方案

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

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

  
Run Code Online (Sandbox Code Playgroud)

  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

  
Run Code Online (Sandbox Code Playgroud)

  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`)
}
Run Code Online (Sandbox Code Playgroud)

  1. 如果您知道您尝试使用的密钥永远不会是继承属性的名称(例如,它们可能是数字,或者它们都具有相同的前缀等),您可以选择使用原始解决方案。


sha*_*eoh 9

您可以使用Lodash get这样进行

_.get(object, 'a[0].b.c');
Run Code Online (Sandbox Code Playgroud)


Mr *_* Br 8

更新

我考虑了以下意见并表示同意。避免评估。

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

// Accessing root property
var rootProp = 'rootPropert';
_.get(object, rootProp, defaultValue);

// Accessing nested property
var listOfNestedProperties = [var1, var2];
_.get(object, listOfNestedProperties);
Run Code Online (Sandbox Code Playgroud)

Lodash get可以以多种方式使用,这是指向lodash.get文档的链接

  • 对访问属性如此微不足道的东西使用`eval`显然是过大的,在任何情况下都不太可取。什么是“麻烦”?obj ['nested'] ['test']`效果很好,不需要您将代码嵌入字符串中。 (8认同)
  • 最好尽可能避免使用`eval`。http://stackoverflow.com/questions/86513/why-is-using-the-javascript-eval-function-a-bad-idea (4认同)
  • 评估速度要慢三倍甚至更多,我不建议新手使用它,因为它可能会教给他们坏习惯。我用``obj ['nested'] ['value']``-记住孩子们,eval是邪恶的! (3认同)
  • @Luke 他现在是唯一想将 Lodash `_.get` 带到谈判桌上的人。我认为这个答案现在应该投赞成票而不是反对票。这可能有点矫枉过正,但很高兴知道它的存在。 (2认同)
  • 感谢您为此介绍 lodash。我通过谷歌来到这里寻找一种在对象深处设置值的方法,并使用了他们的 _.set 方法(与上面相同,但对要设置的值有额外的争论)。 (2认同)

Luk*_*uke 6

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

这是我想要从中获取数据的语言文件对象的示例:

const locs = {
  "audioPlayer": {
    "controls": {
      "start": "start",
      "stop": "stop"
    },
    "heading": "Use controls to start and stop audio."
  }
}
Run Code Online (Sandbox Code Playgroud)

我希望能够传递一个数组,例如: ["audioPlayer", "controls", "stop"] 来访问语言文本,在这种情况下是 "stop"。

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

function getText(selectionArray, obj) {
  selectionArray.forEach(key => {
    obj = obj[key];
  });
  return obj;
}
Run Code Online (Sandbox Code Playgroud)

用法:

/* returns 'stop' */
console.log(getText(["audioPlayer", "controls", "stop"], locs)); 

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


Rup*_*wal 5

每当需要动态访问属性时,都必须使用方括号而不是“”来访问属性。运算符
语法:object [propery}

const something = { bar: "Foobar!" };
const foo = 'bar';
// something.foo; -- not correct way at it is expecting foo as proprty in  something={ foo: "value"};
// correct way is  something[foo]
alert( something[foo])
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

311087 次

最近记录:

6 年,2 月 前