正在检查属性的属性是否存在

Checking if property of property exists

本文关键字:属性 是否 存在 检查      更新时间:2023-09-26

一般来说,如果我想检查obj.foo.bar是否退出,我会这样做:

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

这是相当丑陋和重复,可悲的是,我不得不经常使用它。

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

那么,有没有什么聪明的方法可以检查obj.foo.bar是否退出?

编辑:

  1. 使用字符串解析(或仅将变量作为字符串传递)的方法是非常有问题的,因为可能正在使用minimier
  2. CCD_ 6可用CCD_。这个解决方案仍然不能让我满意

编辑2-解决方案:

如这里建议的

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.Crowder的解决方案,而不是@somethinghere的解决方案。因为最终常规解决方案更短,而且try catch语句还有其他用途,可能会导致问题

你可以让它不那么难看,因为你不需要typeof来完成大部分:

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

如果obj.foo.bar不能是伪值(0""NaNnullundefinedfalse),则最后一项也不需要typeof

if (obj && obj.foo && obj.foo.bar) {
    action();
}

如果你真的想要一个isDefined函数,你必须按照这个问题及其答案中的概述进行字符串解析,但如果你使用一个重命名(缩短)属性名的迷你程序,并且确实涉及字符串解析,那可能会有问题。

您实际上可以将其作为一个通用函数:

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'

你总是需要传递一个参数才能使其工作,但如果你想要全局存在,你可以传递窗口对象。

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'));

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

更新

我注意到设置为false的值也会返回false,所以null是我们的救星。

更新2

既然你提出了minimier问题,有一种方法是半优雅的,可以进行单行部署,但你不能将其封装在一个函数中:

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测试嵌套对象密钥的存在

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

像这样的东西应该可以做到:

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

Js在此报价

小心使用可能会更改密钥名称的缩小程序。