使用点符号字符串访问对象子属性

IT技术 javascript jquery
2021-02-09 12:19:53

我暂时被一个看似非常简单的 JavaScript 问题困住了,但也许我只是缺少正确的搜索关键字!

假设我们有一个对象

var r = { a:1, b: {b1:11, b2: 99}};

有几种方法可以访问99:

r.b.b2
r['b']['b2']

我想要的是能够定义一个字符串

var s = "b.b2";

然后使用访问 99

r.s or r[s] //(which of course won't work)

一种方法是为它编写一个函数,在点上拆分字符串,并可能递归/迭代地获取属性。但是有没有更简单/更有效的方法?这里的任何 jQuery API 中有什么有用的吗?

6个回答

这是我前一段时间写的一个简单的函数,但它适用于基本的对象属性:

function getDescendantProp(obj, desc) {
    var arr = desc.split(".");
    while(arr.length && (obj = obj[arr.shift()]));
    return obj;
}

console.log(getDescendantProp(r, "b.b2"));
//-> 99

尽管有一些答案将其扩展为“允许”数组索引访问,但这并不是真正必要的,因为您可以使用此方法使用点表示法指定数字索引:

getDescendantProp({ a: [ 1, 2, 3 ] }, 'a.2');
//-> 3
@user1912899:这不太一样,但可能更健壮一点,因为它会抛出错误(而我的只会返回undefined)。我想这取决于你的喜好。JSHint 只是抱怨循环条件中的赋值,可以使用boss选项禁用它
2021-03-21 12:19:53
你可以做 while(arr.length && obj) { obj = obj[arr.shift()]; }
2021-03-21 12:19:53
jshint 告诉我重写:while(arr.length) { obj = obj[arr.shift()]; }
2021-03-22 12:19:53
a[1] 不起作用:(
2021-03-25 12:19:53
@JuanDavid:不,这不像 eval 语句,它只是一个简单的拆分和循环。您可以使用a.1, 除非您有一些带有 '.' 的属性。在名字里。如果是这种情况,您将需要更复杂的解决方案。
2021-03-30 12:19:53

对象作为对象传递时拆分减少initalValue

var r = { a:1, b: {b1:11, b2: 99}};
var s = "b.b2";

var value = s.split('.').reduce(function(a, b) {
  return a[b];
}, r);

console.log(value);

更新 (感谢 TeChn4K 发表的评论)

使用 ES6 语法,它甚至更短

var r = { a:1, b: {b1:11, b2: 99}};
var s = "b.b2";

var value = s.split('.').reduce((a, b) => a[b], r);

console.log(value);

@TeChn4K,感谢您的提示!更新了我的答案:-)
2021-03-11 12:19:53
很好的答案,谢谢(永远不要在没有学到新东西的情况下睡觉)
2021-03-21 12:19:53
@AmmarCSE 如果嵌套属性之一是数组,这会起作用吗?例如var key = "b.b2[0].c"
2021-03-29 12:19:53
ES6 语法更短: let value = s.split('.').reduce((a, b) => a[b], r)
2021-04-05 12:19:53
对于您可能尝试查找对象的不存在子值的情况的小改进,即:c.c1要捕捉到这一点,只需在返回值中添加一个检查,如下所示:return (a != undefined) ? a[b] : a ;
2021-04-09 12:19:53

您可以使用lodash get() 和 set() 方法

得到

var object = { 'a': [{ 'b': { 'c': 3 } }] };

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

环境

var object = { 'a': [{ 'b': { 'c': 3 } }] };

_.set(object, 'a[0].b.c', 4);
console.log(object.a[0].b.c);
// → 4
很好,使用 lodash 真的很简单
2021-03-20 12:19:53

如果在您的场景中可以将整个数组变量放入字符串中,则可以使用该eval()函数。

var r = { a:1, b: {b1:11, b2: 99}};
var s = "r.b.b2";
alert(eval(s)); // 99

我能感觉到人们惊恐万分

哦,我知道eval(). 事实上,我要去参加钢丝绒淋浴,因为即使推荐它,我也觉得很脏。安迪,我已经 +1 了你的答案,因为它很容易成为这里最优雅的答案。
2021-03-11 12:19:53
所述卷轴问题,不幸的是,使用eval可以防止作出某些词汇的优化编译器。这意味着,不仅它eval本身很慢,它还会减慢它周围的代码。哦......还有双关语。
2021-03-19 12:19:53
谢谢,这非常简洁 - 但根据安迪的评论,我无法承受性能下降,因为脚本做了很多事情。
2021-03-26 12:19:53
+1 用于预测我的眩晕。
2021-03-29 12:19:53
var getObjectValue = function getter(object, key) { var value; if (typeof object === 'object' && typeof key === 'string') { value = eval('object' + '.' + key); } 返回值;}
2021-04-09 12:19:53

扩展@JohnB 的答案,我还添加了一个 setter 值。查看 plunkr

http://plnkr.co/edit/lo0thC?p=preview

在此处输入图片说明

function getSetDescendantProp(obj, desc, value) {
  var arr = desc ? desc.split(".") : [];

  while (arr.length && obj) {
    var comp = arr.shift();
    var match = new RegExp("(.+)\\[([0-9]*)\\]").exec(comp);

    // handle arrays
    if ((match !== null) && (match.length == 3)) {
      var arrayData = {
        arrName: match[1],
        arrIndex: match[2]
      };
      if (obj[arrayData.arrName] !== undefined) {
        if (typeof value !== 'undefined' && arr.length === 0) {
          obj[arrayData.arrName][arrayData.arrIndex] = value;
        }
        obj = obj[arrayData.arrName][arrayData.arrIndex];
      } else {
        obj = undefined;
      }

      continue;
    }

    // handle regular things
    if (typeof value !== 'undefined') {
      if (obj[comp] === undefined) {
        obj[comp] = {};
      }

      if (arr.length === 0) {
        obj[comp] = value;
      }
    }

    obj = obj[comp];
  }

  return obj;
}
我试图调整 Andy E 的解决方案以获得一套,然后我向下滚动并找到了这个。插入这个功能,看着我所有的测试都变绿了。谢谢。
2021-03-23 12:19:53
感谢您添加设置功能。
2021-04-09 12:19:53