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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14782232/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 17:54:04  来源:igfitidea点击:

How to avoid 'cannot read property of undefined' errors?

javascript

提问by Ari

In my code, I deal with an array that has some entries with many objects nested inside one another, where as some do not. It looks something like the following:

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

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

This is giving me problems because I need to iterate through the array at times, and the inconsistency is throwing me errors like so:

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

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

I am aware that I can say if(a.b){ console.log(a.b.c)}, but this is extraordinarily tedious in cases where there are up to 5 or 6 objects nested within one another. Is there any other (easier) way that I can have it ONLY do the console.log if it exists, but without throwing an error?

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

采纳答案by str

Update:

更新

  • If you use JavaScript according to ECMAScript 2020 or later, see optional chaining.
  • TypeScript has added support for optional chaining in version 3.7.
  • 如果您根据 ECMAScript 2020 或更高版本使用 JavaScript,请参阅optional chaining
  • TypeScript 在3.7版中添加了对可选链接的支持。
// use it like this
obj?.a?.lot?.of?.properties


Solution for JavaScript before ECMASCript 2020 or TypeScript older than version 3.7:

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

A quick workaround is using a try/catch helper function with ES6 arrow function:

一个快速的解决方法是使用带有 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');

Working snippet:

工作片段:

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

See this articlefor details.

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

回答by Benjamin Gruenbaum

What you are doing raises an exception (and rightfully so).

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

You can always do

你总能做到

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

But I wouldn't, instead think of your use case.

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

Why are you accessing data, 6 levels nested that you are unfamiliar of? What use case justifies this?

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

Usually, you'd like to actually validate what sort of object you're dealing with.

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

Also, on a side note you should not use statements like if(a.b)because it will return false if a.b is 0 or even if it is "0". Instead check if a.b !== undefined

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

回答by matt weiss

If I am understanding your question correctly, you want the safest way to determine if an object contains a property.

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

The easiest way is using the "in" statement.

最简单的方法是使用“in”语句。

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

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

Of course you can nest this as deep as you want

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

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

Not sure if this helps.

不确定这是否有帮助。

回答by tehwalris

If you are using lodash, you could use their "has" function. It is similar to the native "in", but allows paths.

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

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

回答by Sean Chen

Try this. If a.bis undefined, it will leave the ifstatement without any exception.

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

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

回答by Maelkhor

This is a common issue when working with deep or complex json object, so I try to avoid try/catch or embedding multiple checks which would make the code unreadable, I usually use this little piece of code in all my procect to do the job.

这是处理深度或复杂 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;
}

回答by Brandon Dyer

If you have lodashyou can use its .getmethod

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

_.get(a, 'b.c.d.e')

or give it a default value

或者给它一个默认值

_.get(a, 'b.c.d.e', default)

回答by martinedwards

I use undefsafereligiously. It tests each level down into your object until it either gets the value you asked for, or it returns "undefined". But never errors.

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

回答by rishat

If you use Babel, you can already use the optional chaining syntax with @babel/plugin-proposal-optional-chaining Babel plugin. This would allow you to replace this:

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

console.log(a && a.b && a.b.c);

with this:

有了这个:

console.log(a?.b?.c);

回答by Hardy Le Roux

I like Cao Shouguang's answer, but I am not fond of passing a function as parameter into the getSafe function each time I do the call. I have modified the getSafe function to accept simple parameters and pure ES5.

我喜欢曹寿光的回答,但我不喜欢每次调用时都将函数作为参数传递给 getSafe 函数。我修改了 getSafe 函数以接受简单的参数和纯 ES5。

/**
* Safely get object properties.    
* @param {*} prop The property of the object to retrieve
* @param {*} defaultVal The value returned if the property value does not exist
* @returns If property of object exists it is returned, 
*          else the default value is returned.
* @example
* var myObj = {a : {b : 'c'} };
* var value;
* 
* value = getSafe(myObj.a.b,'No Value'); //returns c 
* value = getSafe(myObj.a.x,'No Value'); //returns 'No Value'
* 
* if (getSafe(myObj.a.x, false)){ 
*   console.log('Found')
* } else {
*  console.log('Not Found') 
* }; //logs 'Not Found'
* 
* if(value = getSafe(myObj.a.b, false)){
*  console.log('New Value is', value); //logs 'New Value is c'
* }
*/
function getSafe(prop, defaultVal) {
  return function(fn, defaultVal) {
    try {
      if (fn() === undefined) {
        return defaultVal;
      } else {
        return fn();
      }
    } catch (e) {
      return defaultVal;
    }
  }(function() {return prop}, defaultVal);
}