检查属性的属性是否存在

时间:2015-03-24 10:04:37

标签: javascript

通常,如果我想检查obj.foo.bar是否退出,我会这样做:

if(
    typeof obj != "undefined" &&
    typeof obj.foo !== "undefined" &&
    typeof obj.foo.bar !== "undefined"
 ){ action() } 

这是非常丑陋和重复的,遗憾的是我必须经常使用它。

我相信没有任何函数isDefined()可以这样做,因为即使是空函数(function isDefined(){})也会在if(isDefined(obj.foo.bar)){action()}

时抛出错误

那么,有没有聪明的方法来检查obj.foo.bar退出?

编辑:

  1. 使用字符串解析(或只是将变量作为字符串传递)的方法非常有问题 - 因为minifier可能正在使用中。
  2. typeof obj != "undefined"可以替换为obj。这个解决方案仍然不能让我满意。
  3. 编辑2 - 解决方案:

    建议here

    var a = {
        b: {
            c: 'd'
        }
    };
    
    function isDefined(fn) {
        var value;
        try {
            value = fn();
        } catch (e) {
            value = undefined;
        } finally {
            return value !== undefined;
        }
    };
    
    // ES5
    console.log(
        isDefined(function () { return a.b.c; }), //return true
        isDefined(function () { return a.b.c.d.e.f; }) //returns false
    );
    // ES6 using the arrow function
    console.log(
        isset(() => a.b.c),
        isset(() => a.b.c.d.e.f)
    );
    

    我已批准@ T.J.克劳德的解决方案,而不是@ somethinghere的解决方案,因为最终常规解决方案更短,并且因为try catch语句有其他目的并且可能导致问题

3 个答案:

答案 0 :(得分:4)

你可以减少丑陋,因为大部分都不需要typeof

if (obj && obj.foo && typeof obj.foo.bar !== "undefined") {
    action();
}

如果 obj.foo.bar不能是假名(0""NaNnull,{{1} },或undefined),您最后一项不需要false

typeof

如果你真的想要一个if (obj && obj.foo && obj.foo.bar) { action(); } 函数,你必须进入字符串解析as outlined in this other question and its answers,但如果你使用一个重命名(缩短)属性名称的minifier,这可能是一个问题,并且确实涉及字符串解析。

答案 1 :(得分:1)

你实际上可以将它作为一个通用功能:

function isDefined(base){
    // Run through any number of passed arguments, ignoring the first one (`base`)
    for(var i = 1; i < arguments.length; i++){
        // While running, see if they exist and assign it to base, then continue
        base = typeof base[arguments[i]] != "undefined" 
            ? base[arguments[i]] : null;
        // if base is set to false break the loop as theres nothing deeper to find
        if(!base) break;
    }
    // return the value that base was set to
    return base;
}

然后像这样称呼它:

var bar = {foo: {}};
console.log(isDefined(bar, 'foo')); ///= Will be bar[foo] (Object Object)
console.log(isDefined(bar, 'foo', 't')); // Will be 'null'

您将始终必须传递一个参数才能使其工作,但如果您想要全局存在,则可以传递窗口对象。

&#13;
&#13;
function isDefined(base){
    for(var i = 1; i < arguments.length; i++){
    	base = typeof base[arguments[i]] != "undefined" 
    		? base[arguments[i]] : null;
    	if(!base) break;
    }
    return base;
}


var bar = {foo: {}};
document.write(isDefined(bar, 'foo') + " / " + isDefined(bar, 'foo', 't'));
&#13;
&#13;
&#13;

如果您想先查看bar是否存在,请尝试使用isDefined(window, 'bar', 'foo');

更新

我注意到设置为false的值也将返回false,因此null是我们的救星。

更新2

由于你提出了minifier问题,有一种方法是半优雅的,允许单行部署,但你不能将它包装在一个函数中:

var bar = {};
try { var defined = typeof bar.foo !== "undefined"; } catch(e) { var defined = false; } // defined will be false
var bar = {foo:{}};
try { var defined = typeof bar.foo !== "undefined"; } catch(e) { var defined = false; } // defined will be true

令人讨厌的是它需要在你的if之前执行,但它确实简化了你的if

var bar = {};
try { var defined = typeof bar.foo !== "undefined"; } catch(e) { var defined = false; }
if(defined){
    // Code here
}

希望有所帮助。

更新3

毕竟有一种方法可以将它包装在一个函数中,隐藏得非常深,但这里是被低估的答案的直接链接:javascript test for existence of nested object key

关键是将值包装在一个返回当时值的函数中。它非常聪明!

答案 2 :(得分:0)

这样的事情可以解决问题:

function isDefined(obj, keysString) {
    //get all the keys
    var keys = keysString.split('.');
    var tmp = obj;

    for(var i = 0; i < keys.length; i++) {
        tmp = tmp[keys[i]];

        //slowly construct your 'deep' object while checking if you can       
        if(typeof tmp === 'undefined') {
            return false;
        }

    }

    return true;
}

用法:

isDefined({test: {nested: true}}, 'test.nested'); //return true
isDefined({test: {nested: true}}, 'test.nested.supernested'); //return false
isDefined({test: {nested: true}}, 'test.othernested.supernested'); //return false

JsFiddle Here

小心可能会更改按键名称的缩小器。