如果我有对象的引用:

var test = {};

可能(但不是立即)具有嵌套对象,例如:

{level1: {level2: {level3: "level3"}}};

检查深度嵌套对象中是否存在属性的最佳方法是什么?

警报(测试级别1);生成未定义,但警告(test.level1.level2.level3);失败。

我目前正在做这样的事情:

if(test.level1 && test.level1.level2 && test.level1.level2.level3) {
    alert(test.level1.level2.level3);
}

但我想知道是否有更好的方法。


当前回答

在html模板中使用一行程序并没有真正好的答案,所以我使用ES6代理做了一个。您只需将一个对象或值传递给“遍历”函数,并使用返回值或回退值的函数调用来完成所需的嵌套调用。使用:

const testObject={深度:{嵌套:{对象:{closure:()=>{return“closure”},数量:9,布尔值:真,数组:[1,2,{foo:{bar:true}}]} }}}遍历(testObject).dep()//{嵌套:{…}}遍历(testObject).non.existint()//未定义的遍历(testObject).dep.enested.obj.closure()//关闭遍历(testObject).dep.enested.obj.array[5]('fallback')//后退,后退遍历(testObject).dep.enested.obj.array[2]()//{foo:{…}}遍历(testObject).dep.enested.obj.array[2].foo.bar()//真的遍历(testObject).dept.netnested.obj.array[2].foo.bar[4]('fallback')//后退,后退遍历(testObject).完全.错误[3].调用().WILL_THROW()//未捕获的TypeError:无法读取未定义的属性“WILL_TROW”

函数本身:

常量遍历=(输入)=>{//唯一空对象const unset=新对象();//我们需要包装器来确保我们可以访问同一个唯一的空对象常量闭包=(输入)=>{//将每个输入包装到此const handler=new Function();handler.input=输入;//返回包装器代理返回新代理(handler{//保持横向移动获取:(target,name)=>{//如果未定义,则作为初始输入提供if(!target.input){返回关闭(未设置);}//否则if(target.input[name]!==未定义){//输入具有该属性返回闭包(target.input[name]);}其他{返回关闭(未设置);}},//带回退的结果apply:(目标,上下文,参数)=>{return handler.input==未设置?args[0]:handler.input;}})}返回闭合(输入);}

其他回答

我认为下面的脚本提供了更可读的表示。

声明函数:

var o = function(obj) { return obj || {};};

然后这样使用:

if (o(o(o(o(test).level1).level2).level3)
{

}

我称之为“悲伤小丑技巧”,因为它使用了符号o(


编辑:

这是TypeScript的版本

它在编译时提供类型检查(如果使用像Visual Studio这样的工具,还提供智能感知)

export function o<T>(someObject: T, defaultValue: T = {} as T) : T {
    if (typeof someObject === 'undefined' || someObject === null)
        return defaultValue;
    else
        return someObject;
}

用法相同:

o(o(o(o(test).level1).level2).level3

但这次intelligense奏效了!

此外,您可以设置默认值:

o(o(o(o(o(test).level1).level2).level3, "none")

我编写了一个名为l33teral的库来帮助测试嵌套财产。您可以这样使用:

var myObj = {/*...*/};
var hasNestedProperties = leet(myObj).probe('prop1.prop2.prop3');

我也很喜欢这里的ES5/6解决方案。

我创建了一个小函数来安全地获取嵌套对象财产。

function getValue(object, path, fallback, fallbackOnFalsy) {
    if (!object || !path) {
        return fallback;
    }

    // Reduces object properties to the deepest property in the path argument.
    return path.split('.').reduce((object, property) => {
       if (object && typeof object !== 'string' && object.hasOwnProperty(property)) {
            // The property is found but it may be falsy.
            // If fallback is active for falsy values, the fallback is returned, otherwise the property value.
            return !object[property] && fallbackOnFalsy ? fallback : object[property];
        } else {
            // Returns the fallback if current chain link does not exist or it does not contain the property.
            return fallback;
        }
    }, object);
}

或者更简单但稍微不可读的版本:

function getValue(o, path, fb, fbFalsy) {
   if(!o || !path) return fb;
   return path.split('.').reduce((o, p) => o && typeof o !== 'string' && o.hasOwnProperty(p) ? !o[p] && fbFalsy ? fb : o[p] : fb, o);
}

或者更短,但没有错误标志:

function getValue(o, path, fb) {
   if(!o || !path) return fb;
   return path.split('.').reduce((o, p) => o && typeof o !== 'string' && o.hasOwnProperty(p) ? o[p] : fb, o);
}

我测试了:

const obj = {
    c: {
        a: 2,
        b: {
            c: [1, 2, 3, {a: 15, b: 10}, 15]
        },
        c: undefined,
        d: null
    },
    d: ''
}

下面是一些测试:

// null
console.log(getValue(obj, 'c.d', 'fallback'));

// array
console.log(getValue(obj, 'c.b.c', 'fallback'));

// array index 2
console.log(getValue(obj, 'c.b.c.2', 'fallback'));

// no index => fallback
console.log(getValue(obj, 'c.b.c.10', 'fallback'));

要查看所有包含文档和测试的代码,您可以查看我的github要点:https://gist.github.com/vsambor/3df9ad75ff3de489bbcb7b8c60beebf4#file-javascriptgetnestedvalues js

如果您恰好使用AngularJs,则可以使用$parse服务检查是否存在深度对象属性,如下所示:

if( $parse('model.data.items')(vm) ) {
    vm.model.data.items.push('whatever');
}

为了避免这样的陈述:

if(vm.model && vm.model.data && vm.model.data.items) {
    ....
}

不要忘记将$parse服务注入控制器

有关详细信息:https://glebbahmutov.com/blog/angularjs-parse-hacks/

还有一个非常紧凑的:

function ifSet(object, path) {
  return path.split('.').reduce((obj, part) => obj && obj[part], object)
}

打电话:

let a = {b:{c:{d:{e:'found!'}}}}
ifSet(a, 'b.c.d.e') == 'found!'
ifSet(a, 'a.a.a.a.a.a') == undefined

它的性能不会很好,因为它拆分了一个字符串(但增加了调用的可读性),并遍历所有内容,即使已经很明显找不到任何内容(但提高了函数本身的可读性。

至少比get快http://jsben.ch/aAtmc