17

我有基于 json 的数据结构,其中包含嵌套对象的对象。为了访问特定的数据元素,我将对象属性的引用链接在一起。例如:

var a = b.c.d;

如果 b 或 bc 未定义,这将失败并出现错误。但是,如果它存在,我想获得一个值,否则只是未定义。无需检查链中的每个值是否都存在的最佳方法是什么?

我想尽可能地保持这种方法的通用性,这样我就不必添加大量的辅助方法,例如:

var a = b.getD();

或者

var a = helpers.getDFromB(b);

我还想尽量避免使用 try/catch 构造,因为这不是错误,所以使用 try/catch 似乎是错误的。这合理吗?

有任何想法吗?

4

13 回答 13

16

标准方法:

var a = b && b.c && b.c.d && b.c.d.e;

非常快但不太优雅(特别是对于较长的属性名称)。

使用函数遍历 JavaScipt 对象属性既不高效也不优雅。

试试这个:

try { var a = b.c.d.e; } catch(e){}

如果您确定a以前没有使用过或

try { var a = b.c.d.e; } catch(e){ a = undefined; }

以防您以前分配过它。

这可能比第一个选项更快。

于 2014-03-28T20:31:13.633 回答
8

ECMAScript2020在 Node v14 中具有可选的链接运算符(我已经看到它也称为安全导航运算符),这将允许您的示例编写为:

var a = b?.c?.d;

来自MDN 文档

可选的链接运算符 (?.) 允许读取位于连接对象链深处的属性值,而无需明确验证链中的每个引用是否有效。这 ?。运算符的功能与 . 链接运算符,除了如果引用为空(null 或未定义)时不会导致错误,表达式会短路并返回未定义的值。与函数调用一起使用时,如果给定函数不存在,则返回 undefined。

于 2020-11-30T04:09:46.247 回答
5

您可以创建一个通用方法,该方法基于属性名称数组访问元素,该数组被解释为通过属性的路径:

function getValue(data, path) {
    var i, len = path.length;
    for (i = 0; typeof data === 'object' && i < len; ++i) {
        data = data[path[i]];
    }
    return data;
}

然后你可以调用它:

var a = getValue(b, ["c", "d"]);
于 2013-08-12T01:40:18.433 回答
3

这是一个老问题,现在有了 es6 的特性,这个问题可以更容易地解决。

const idx = (p, o) => p.reduce((xs, x) => (xs && xs[x]) ? xs[x] : null, o);

感谢@sharifsbeat 提供此解决方案

于 2017-07-02T17:05:21.043 回答
2

可能很简单:

let a = { a1: 11, b1: 12, c1: { d1: 13, e1: { g1: 14 }}}
console.log((a || {}).a2); => undefined
console.log(((a || {}).c1 || {}).d1) => 13

等等。

于 2017-11-01T07:25:13.647 回答
2

这里的答案是很好的裸机解决方案。但是,如果您只想使用经过验证的包,我建议使用 lodash。

使用 ES6,您可以运行以下命令

import _ from 'lodash'

var myDeepObject = {...}

value = _.get(myDeepObject, 'maybe.these.path.exist', 'Default value if not exists')

于 2019-12-20T09:16:40.653 回答
2

ES6 有可选的链接,可以按如下方式使用:

const object = { foo: {bar: 'baz'} };

// not found, undefined
console.log(object?.foo?.['nested']?.missing?.prop)

// not found, object as default value
console.log(object?.foo?.['nested']?.missing?.prop || {})

// found, "baz"
console.log(object?.foo?.bar)

这种方法需要定义变量“object”并使其成为一个对象。

或者,您可以定义自己的实用程序,这是一个实现递归的示例:

const traverseObject = (object, propertyName, defaultValue) => {
  if (Array.isArray(propertyName)) {
    return propertyName.reduce((o, p) => traverseObject(o, p, defaultValue), object);
  }

  const objectSafe = object || {};

  return objectSafe[propertyName] || defaultValue;
};

// not found, undefined
console.log(traverseObject({}, 'foo'));

// not found, object as default value
console.log(traverseObject(null, ['foo', 'bar'], {}));

// found "baz"
console.log(traverseObject({foo: {bar:'baz'}}, ['foo','bar']));

于 2021-09-29T09:56:27.840 回答
0
const getValue = (obj, property, defaultValue) => (
  property.split('.').reduce((item, key) => {
    if (item && typeof item === 'object' && key in item) {
      return item[key];
    }
    return defaultValue;
  }, obj)
)

常量对象 = { 'a': { 'b': { 'c': 3 } } };

getValue(object, 'a.b.c'); // 3
getValue(object, 'a.b.x'); // undefined
getValue(object, 'a.b.x', 'default'); // 'default'
getValue(object, 'a.x.c'); // undefined
于 2020-03-27T10:24:32.250 回答
0

我将粘贴我在几乎所有项目中使用的功能作为此类情况的实用程序。

public static is(fn: Function, dv: any) {
    try {
        if (fn()) {
                return fn()
            } else {
                return dv
            }
        } catch (e) {
            return dv
        }
    }

所以第一个参数是回调,第二个是默认值,如果由于某些错误而无法提取数据。

我在所有地方都这样称呼它:

var a = is(()=> a.b.c, null);
于 2020-06-09T11:10:05.973 回答
-1

获取 处的pathobject。如果解析的值为undefineddefaultValue则在其位置返回 。

在 ES6 中,我们可以从Object类似下面的代码片段中获取嵌套属性。

const myObject = {
    a: {
      b: {
        c: {
          d: 'test'
        }
      }
    },
    c: {
      d: 'Test 2'
    }
  },

  isObject = obj => obj && typeof obj === 'object',

  hasKey = (obj, key) => key in obj;



function nestedObj(obj, property, callback) {
  return property.split('.').reduce((item, key) => {
    if (isObject(item) && hasKey(item, key)) {
      return item[key];
    }
    return typeof callback != undefined ? callback : undefined;
  }, obj);
}

console.log(nestedObj(myObject, 'a.b.c.d')); //return test


console.log(nestedObj(myObject, 'a.b.c.d.e')); //return undefined


console.log(nestedObj(myObject, 'c.d')); //return Test 2


console.log(nestedObj(myObject, 'd.d', false)); //return false


console.log(nestedObj(myObject, 'a.b')); //return {"c": {"d": "test"}}

于 2018-03-26T10:57:04.950 回答
-1

一个老问题,现在我们经常有 Typescript 项目,以至于这个问题似乎无关紧要,但我来这里是为了寻找同样的东西,所以我做了一个简单的函数来做。你关于不使用 try/catch 的想法对我来说太严格了,毕竟 seek forundefined.x无论如何都会导致错误。因此,这就是我的方法。

function getSafe (obj, valuePath) {
    try { return eval("obj." + valuePath); } 
    catch (err) { return null; }
}

要使用它,我们必须传递对象。我试图避免这种情况,但没有其他方法可以从另一个函数中获取范围(这里有很多关于这个的问题)。还有一个小测试集来看看我们得到了什么:

let outsideObject = {
    html: {
        pageOne: {
            pageTitle: 'Lorem Ipsum!'
        }
    }
};
function testme() {  
    let insideObject = { a: { b: 22 } };
    return {
        b: getSafe(insideObject, "a.b"),       // gives: 22
        e: getSafe(insideObject, "a.b.c.d.e"), // gives: null
        pageTitle: getSafe(outsideObject, "html.pageOne.pageTitle"),     // gives: Lorem Ipsum!
        notThere: getSafe(outsideObject, "html.pageOne.pageTitle.style") // gives: undefined
    }
}
testme(); 

更新:关于使用,eval我认为 eval 是一个谨慎使用的工具,而不是魔鬼本身。在这种方法中,用户不会干扰 eval,因为开发人员正在通过名称查找属性。

于 2019-09-25T18:45:12.063 回答
-1

如果您关心语法,这里是 Hosar 答案的更简洁版本:

function safeAccess(path, object) {
  if (object) {
    return path.reduce(
      (accumulator, currentValue) => (accumulator && accumulator[currentValue] ? accumulator[currentValue] : null),
      object,
    );
  } else {
    return null;
  }
}
于 2019-10-25T18:51:19.557 回答
-1

如果你想要动态访问手头上不规则数量的属性,在 ES6 中你可以很容易地执行以下操作;

function getNestedValue(o,...a){
  var val = o;
  for (var prop of a) val = typeof val === "object" &&
                                   val !== null     &&
                                   val[prop] !== void 0 ? val[prop]
                                                        : undefined;
  return val;
}
var obj = {a:{foo:{bar:null}}};
console.log(getNestedValue(obj,"a","foo","bar"));
console.log(getNestedValue(obj,"a","hop","baz"));

于 2016-10-14T10:25:03.953 回答