JavaScript, elegant way to check nested object properties for null/undefined
我偶尔会遇到一个"问题",就是我有一个对象,例如: user = {}并且通过使用应用程序的过程,这将填充。 让我们说一下,在AJAX调用之后或者我这样做的事情:
1 2 3 4
| user.loc = {
lat: 50,
long: 9
} |
在另一个地方,我想检查user.loc.lat是否存在。
1 2 3
| if (user.loc.lat) {
// do something
} |
如果它不存在,这将导致错误。 如果user.loc.lat是undefined,user.loc当然也是undefined。
1
| "Cannot read property 'lat' of null" - Dev Tools error |
这意味着我需要像这样检查:
1 2 3 4 5
| if (user.loc) {
if (user.loc.lat) {
// do something
}
} |
要么
1 2 3
| if (user.loc && user.loc.lat) {
// do something
} |
这不是很漂亮,我的对象越大越好 - 显然(想象10级嵌套)。
有点遗憾的是,如果user.loc undefined,if(user.loc.lat)不仅仅是返回false。
检查这种情况的理想方法是什么?
-
试试这个if(user && user.loc && user.loc.lat) {
-
您可以使用typeof检查null和undefined的值
-
@ AamirAfridi - 即使这是OP的编写方式,测试应该停在最后一个对象,而不是属性。 例如 如果user.loc.lat存在但是具有假值(例如0),则测试将返回false。:-)
您可以使用这样的实用程序函数:
1 2 3 4 5
| get = function(obj, key) {
return key.split(".").reduce(function(o, x) {
return (typeof o =="undefined" || o === null) ? o : o[x];
}, obj);
} |
用法:
1 2
| get(user, 'loc.lat') // 50
get(user, 'loc.foo.bar') // undefined |
或者,仅检查属性是否存在,而不检查其值:
1 2 3 4 5 6 7 8 9 10
| has = function(obj, key) {
return key.split(".").every(function(x) {
if(typeof obj !="object" || obj === null || ! x in obj)
return false;
obj = obj[x];
return true;
});
}
if(has(user, 'loc.lat')) ... |
-
爱它!优秀的解决方
-
get方法的问题在于它可能从链中的任何位置返回属性,它不会专门测试最后一个属性。
-
@RobG怎么样?一旦它遇到一个未定义的,只是一直传递,结果是未定义的。
-
@RobG:更新了has以获得更准确的类型检查,这样如果foo.bar是一个数字,has('foo.bar.baz')就不会失败。
-
@ JamesMontagne- get({foo:{bar:{fum:undefined}}},'foo.fog')返回undefined,因为雾不是bar的属性,不是因为它读取了值。同样,应检查属性是否存在,而不是尝试读取值是否返回undefined或null。如果obj是一个函数对象或一个hostof不返回对象的主机对象(在某些浏览器中不需要和不需要),它也会失败。
-
@RobG"get({foo:{bar:{fum:undefined}}},'foo.fog')返回undefined,因为雾不是bar的属性,不是因为它读取了值。" ....因为没有值阅读,该属性将是未定义的...这就是重点。
-
你的回答是"仅检查属性是否存在",但它没有,它会检查返回的值,这是完全不同的。此外,由于运算符优先级,! x in obj被计算为(!x) in obj,因此它检查属性名称"true"或"false",具体取决于!x的结果(由于它是一个字符串,它将始终为true ),因此除非存在属性obj.true,否则该表达式的结果将为false。我认为你的意思是!(x in obj)。
-
我希望lodash会和它的函数。_.has是最接近但不检查值。
-
实际上,刮擦它,它确实!只需使用_.get stackoverflow.com/a/24046380/654708
-
这段代码有缺陷,它不是完全正确的代码是has = function(obj,key){return key.split("。")。every(function(x){if(typeof obj!="object"|| obj = == null ||!(obj中的x))return false; obj = obj [x]; return true;});
-
本文真实地总结了这样做的所有方法:hackernoon.com/…
好吧,javascript有try-catch。 根据您实际需要做的事情(即else语句在undefined之后会是什么样子),这可能就是您想要的。
例:
1 2 3 4 5
| try {
user.loc.lat.doSomething();
} catch(error) {
//report
} |
-
啊,当然。也是一个很好的答案,谢谢!
-
我不认为抛出异常而不是if-else是个好主意,如果你知道var可能没有设置,我不认为是异常,因此你应该处理它而不抛出try-catch。 (当然你可能会抛出else子句)然后。
-
我喜欢这个简单的解决方案的解决方案,有时用条件读取更难。 @JonasStensved指出没有使用try catch,因为启动异常(你想要在你写throw之前没有启动它,但我认为)但问题可能是块中出现了其他问题,你将无法检测到它关于未来。
-
@pikilon经验法则:不要使用异常进行流量控制。在你的情况下,一些事情真的是意外的和异常,然后抛出。否则将其作为任何其他代码条件处理。
您可以使用lazy and组合检查:
1
| if(user.loc && user.loc.lat) { ... |
或者,您使用CoffeeScript并编写
它将运行loc属性的检查并防止空对象。
-
对不起,你们都很快回答。您的第一种方法也是我想避免的情况。考虑到我有5到15级嵌套的巨大对象。
-
你这里没有很多选择。 (1)财产检查,手动或使用CoffeeScript,(2)try / catch。如果你有一个具有15级嵌套的对象,并且它的属性可能在任何级别都不存在,那么我认为你的应用程序有一些次优的设计模式。
-
这是一个例子。我想要一种方法来涵盖这种情况。我显然会避免15级嵌套,但是如果一个旧的服务器端应用程序只是这样做而且我必须处理它呢?这就是我想要涵盖那种情况的原因
试试这个if(user && user.loc && user.loc.lat) {...}
您可以使用typeof检查null和undefined的值
如果.loc的值false,则可以尝试
if(user && user.loc && typeof(user.loc)!=="undefined"){...}
如果你有一个巨大的嵌套对象而不是看看
资源。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function checkNested(obj /*, level1, level2, ... levelN*/) {
var args = Array.prototype.slice.call(arguments),
obj = args.shift();
for (var i = 0; i < args.length; i++) {
if (!obj.hasOwnProperty(args[i])) {
return false;
}
obj = obj[args[i]];
}
return true;
}
var test = {level1:{level2:{level3:'level3'}} };
checkNested(test, 'level1', 'level2', 'level3'); // true
checkNested(test, 'level1', 'level2', 'foo'); // false |
更新:
试试lodash.get
-
对不起,你们都很快回答。这也是我想避免的情况。考虑到我有5到15级嵌套的巨大对象。
-
答案已更新。
-
我讨厌人们投票而不添加评论。