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

Ari*_*Ari 98 javascript

在我的代码中,我处理的数组中有一些条目,其中有许多对象彼此嵌套,而有些则没有.它看起来像下面这样:

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

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

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);
}
Run Code Online (Sandbox Code Playgroud)

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

str*_*str 78

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

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

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

// or add an optional default value
getSafe(() => obj.a.lot.of.properties, 'nothing');
Run Code Online (Sandbox Code Playgroud)

有关详细信息,请参阅此文

  • 我喜欢它!我唯一要添加的是 catch 中的 console.warn ,以便您知道错误但它会继续。 (2认同)
  • **捕获*所有*异常而不重新抛出是不好的**,并且通常使用异常作为预期执行流程的一部分也不是很好——尽管在这种情况下它被很好地包含了。 (2认同)

Ben*_*aum 43

你正在做的事情引发了一个例外(理所当然).

你总能这样做

try{
   window.a.b.c
}catch(e){
   console.log("YO",e)
}
Run Code Online (Sandbox Code Playgroud)

但我不会想到你的用例.

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

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

另外,在旁注中你不应该使用这样的语句,if(a.b)因为如果ab为0或者即使它是"0"它将返回false.而是检查是否a.b !== undefined

  • 如果你不想厌倦ab && abc && console.log(abc),那么这是一致记录未知数的唯一方法. (3认同)
  • 关于您的第一次编辑:这是合理的;我正在处理 JSON 结构化数据库条目,这样对象将缩放多个级别的字段(即条目.users.messages.date 等,其中并非所有情况都输入数据) (2认同)

小智 14

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

最简单的方法是使用"in"语句.

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

if ("b" in window){
     //false
 }
Run Code Online (Sandbox Code Playgroud)

当然,您可以根据需要将其嵌套

if ("a" in window.b.c) { }
Run Code Online (Sandbox Code Playgroud)

不确定这是否有帮助.

  • 您无法安全地将其嵌套到您想要的深度.如果`window.b`未定义怎么办?你会得到一个类型错误:`不能使用'in'运算符来搜索undefined`中的'c' (5认同)

teh*_*ris 12

如果您使用的是lodash,则可以使用"has"功能.它类似于原生的"in",但允许路径.

var testObject = {a: {b: {c: 'walrus'}}};
if(_.has(testObject, 'a.b.c')) {
  //Safely access your walrus here
}
Run Code Online (Sandbox Code Playgroud)

  • 最好,我们可以使用带有默认值的`_.get()`以便于阅读:`_.get(object,'abc','default');` (2认同)

小智 8

尝试这个。如果a.b未定义,它将使该if语句无任何异常。

if (a.b && a.b.c) {
  console.log(a.b.c);
}
Run Code Online (Sandbox Code Playgroud)


ris*_*hat 7

如果你使用 Babel,你已经可以在@babel/plugin-proposal-optional-chaining Babel plugin 中使用可选链接语法。这将允许您替换它:

console.log(a && a.b && a.b.c);
Run Code Online (Sandbox Code Playgroud)

有了这个:

console.log(a?.b?.c);
Run Code Online (Sandbox Code Playgroud)


mar*_*rds 5

我虔诚地使用undefsafe。它测试每个级别到您的对象,直到它获得您要求的值,或者返回“未定义”。但从不出错。

  • 类似于 lodash `_.get` (2认同)

小智 5

这是使用深度或复杂json对象时的常见问题,因此我尝试避免try/catch或嵌入多次检查会导致代码不可读,我通常在我的所有procect中使用这一小段代码来完成这项工作.

/* ex: getProperty(myObj,'aze.xyz',0) // return myObj.aze.xyz safely
 * accepts array for property names: 
 *     getProperty(myObj,['aze','xyz'],{value: null}) 
 */
function getProperty(obj, props, defaultValue) {
    var res, isvoid = function(x){return typeof x === "undefined" || x === null;}
    if(!isvoid(obj)){
        if(isvoid(props)) props = [];
        if(typeof props  === "string") props = props.trim().split(".");
        if(props.constructor === Array){
            res = props.length>1 ? getProperty(obj[props.shift()],props,defaultValue) : obj[props[0]];
        }
    }
    return typeof res === "undefined" ? defaultValue: res;
}
Run Code Online (Sandbox Code Playgroud)


Bra*_*yer 5

如果你有lodash你可以使用它的.get方法

_.get(a, 'b.c.d.e')
Run Code Online (Sandbox Code Playgroud)

或者给它一个默认值

_.get(a, 'b.c.d.e', default)
Run Code Online (Sandbox Code Playgroud)