安全地访问Javascript嵌套对象

时间:2013-08-12 01:30:51

标签: javascript node.js

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

var a = b.c.d;

如果未定义b或b.c,则会失败并显示错误。但是,我希望得到一个值,如果它存在,否则只是未定义。在不检查链中的每个值是否存在的情况下,最好的方法是什么?

我想尽可能保持这种方法的一般性,所以我不必添加大量的辅助方法,如:

var a = b.getD();

var a = helpers.getDFromB(b);

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

有什么想法吗?

12 个答案:

答案 0 :(得分:11)

标准方法:

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

如果您之前已经分配了它。

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

答案 1 :(得分: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"]);

答案 2 :(得分:3)

这是一个老问题,现在有了es6功能,这个问题可以更容易解决。

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

感谢@sharifsbeat为此solution

答案 3 :(得分:0)

如果您希望动态访问手头有不规则数量的属性,那么在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"));

答案 4 :(得分:0)

可能它可能很简单:

{{1}}

等等。

答案 5 :(得分:0)

获取path的{​​{1}}处的值。如果已解析的值为object,则会在其位置返回undefined

  

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

&#13;
&#13;
Object
&#13;
&#13;
&#13;

答案 6 :(得分:0)

一个老问题,现在我们经常有Typescript项目,以至于这个问题似乎无关紧要,但是我到这里来寻找相同的东西,所以我做了一个简单的函数来做。对于我来说,您对不使用try / catch的想法过于严格,毕竟寻找undefined.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是一种要谨慎使用的工具,而不是魔鬼本身。在这种方法中,用户不会干扰评估,因为开发人员正在按其名称查找属性。

答案 7 :(得分:0)

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

function safeAccess(path, object) {
  if (object) {
    return path.reduce(
      (accumulator, currentValue) => (accumulator && accumulator[currentValue] ? accumulator[currentValue] : null),
      object,
    );
  } else {
    return null;
  }
}

答案 8 :(得分:0)

这里的答案是好的裸机解决方案。但是,如果您只想使用经过尝试且正确的软件包,建议您使用lodash。

使用ES6,您可以运行以下

import _ from 'lodash'

var myDeepObject = {...}

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

答案 9 :(得分: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);

答案 10 :(得分:0)

ECMAScript2020,并且在Node v14中具有optional chaining运算符(我也看到它称为安全导航运算符),这可以使您的示例编写为:

var a = b?.c?.d;

来自MDN docs

可选的链接运算符(?。)允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。 ?。操作符的功能类似于。链接运算符,除了在引用为空(空或未定义)的情况下不会引起错误外,表达式会使用返回值未定义来短路。与函数调用一起使用时,如果给定的函数不存在,它将返回undefined。

答案 11 :(得分:-1)

const getValue = (obj, property, defaultValue) => (
  property.split('.').reduce((item, key) => {
    if (item && typeof item === 'object' && key in item) {
      return item[key];
    }
    return defaultValue;
  }, obj)
)

const object = {'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