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
How to avoid 'cannot read property of undefined' errors?
提问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
回答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
回答by martinedwards
回答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);
}

