如何避免“无法读取未定义的属性”错误?

IT技术 javascript
2021-01-31 15:09:09

在我的代码中,我处理了一个数组,该数组有一些条目,其中许多对象相互嵌套,而有些则没有。它看起来类似于以下内容:

// where this array is hundreds of entries long, with a mix
// of the two examples given
var test = [{'a':{'b':{'c':"foo"}}}, {'a': "bar"}];

这给我带来了问题,因为我有时需要遍历数组,而这种不一致给我带来了如下错误:

for (i=0; i<test.length; i++) {
    // ok on i==0, but 'cannot read property of undefined' on i==1
    console.log(a.b.c);
}

我知道我可以说if(a.b){ console.log(a.b.c)},但是在最多有 5 或 6 个对象相互嵌套的情况下,这是非常乏味的。有没有其他(更简单的)方法可以让它只在 console.log 存在的情况下执行,但不会抛出错误?

6个回答

更新

  • 如果您根据 ECMAScript 2020 或更高版本使用 JavaScript,请参阅optional chaining
  • TypeScript 在3.7版中添加了对可选链接的支持
// use it like this
obj?.a?.lot?.of?.properties

ECMASCRipt 2020 之前的 JavaScript 或 TypeScript 3.7 之前的解决方案

一个快速的解决方法是使用带有 ES6箭头函数的 try/catch 辅助函数

function getSafe(fn, defaultVal) {
  try {
    return fn();
  } catch (e) {
    return defaultVal;
  }
}

// use it like this
console.log(getSafe(() => obj.a.lot.of.properties));

// or add an optional default value
console.log(getSafe(() => obj.a.lot.of.properties, 'nothing'));

我喜欢它!我唯一要添加的是 catch 中的 console.warn,以便您知道错误但它会继续。
2021-03-29 15:09:09
不重新抛出的情况下捕获所有异常是不好的,并且通常将异常用作预期执行流程的一部分也不是很好——尽管在这种情况下它被很好地包含了。
2021-03-30 15:09:09

您正在做的事情会引发异常(这是理所当然的)。

你总能做到

try{
   window.a.b.c
}catch(e){
   console.log("YO",e)
}

但我不会,而是考虑您的用例。

你为什么要访问数据,你不熟悉的 6 层嵌套?什么用例证明了这一点?

通常,您希望实际验证您正在处理的对象类型。

另外,附带说明一下,您不应该使用类似的语句,if(a.b)因为如果 ab 为 0 或者即使它是“0”,它也会返回 false。而是检查是否a.b !== undefined

如果您不想单调乏味的 ab && abc && console.log(abc) 那么这是一致记录未知数的唯一方法。
2021-03-10 15:09:09
@Ian 是的,我显然是相反的意思,即使 ab 为“0”,它也会返回 false。不错的收获
2021-03-22 15:09:09
@BenjaminGruenbaum 听起来不错,不确定您是否是这个意思。另外,我认为你想要typeof a.b !== "undefined" && ab!=null` - 注意!==
2021-04-01 15:09:09
关于您的第一次编辑:这是合理的;我正在处理 JSON 结构化数据库条目,这样对象将扩展多个级别的字段(即 entry.users.messages.date 等,并非所有情况都输入了数据)
2021-04-04 15:09:09
“如果 ab 为 0,它将返回 true” - 不。typeof a.b === "undefined" && a.b!=null- 没有必要在第一部分之后做第二部分,这样做更有意义if ("b" in a)
2021-04-06 15:09:09

如果我正确理解您的问题,您需要最安全的方法来确定对象是否包含属性。

最简单的方法是使用in运算符

window.a = "aString";
//window should have 'a' property
//lets test if it exists
if ("a" in window){
    //true
 }

if ("b" in window){
     //false
 }

当然,您可以将它嵌套到您想要的深度

if ("a" in window.b.c) { }

不确定这是否有帮助。

你不能像你想要的那样安全地嵌套它。如果window.b未定义怎么办?你会得到一个类型错误:Cannot use 'in' operator to search for 'c' in undefined
2021-04-09 15:09:09

如果您使用lodash,您可以使用他们的“has”功能。它类似于原生的“in”,但允许路径。

var testObject = {a: {b: {c: 'walrus'}}};
if(_.has(testObject, 'a.b.c')) {
  //Safely access your walrus here
}
最好,我们可以使用_.get()默认值以便于阅读:_.get(object, 'a.b.c', 'default');
2021-03-23 15:09:09

试试这个。如果a.b未定义,它将if毫无例外地离开该语句。

if (a.b && a.b.c) {
  console.log(a.b.c);
}