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

Javascript 如何避免';无法读取未定义的';错误?,javascript,monetjs,Javascript,Monetjs,在我的代码中,我处理一个数组,该数组中有一些条目,其中许多对象嵌套在另一个条目中,而有些条目则没有。它看起来如下所示: // 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

在我的代码中,我处理一个数组,该数组中有一些条目,其中许多对象嵌套在另一个条目中,而有些条目则没有。它看起来如下所示:

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

for(i=0;i您所做的会引发异常(这是理所当然的)

你总能做到

try{
   window.a.b.c
}catch(e){
   console.log("YO",e)
}
但我不会去想你的用例

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

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


另外,在旁注中,您不应该使用类似于
if(a.b)
的语句,因为如果a.b为0,甚至是“0”,它将返回false。相反,请检查
a.b!==未定义的

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

最简单的方法是使用

当然,你可以把它埋得越深越好

if ("a" in window.b.c) { }
不确定这是否有帮助。

如果您正在使用,您可以使用它们的“has”功能。它类似于本机的“in”,但允许路径

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

我很虔诚地使用它。它测试对象的每一个级别,直到它得到您要求的值,或者返回“undefined”。但决不会出错。

这是处理深度或复杂json对象时的常见问题,因此我尽量避免尝试/捕获或嵌入多个检查,这会使代码无法读取,我通常在所有过程中都使用这段代码来完成这项工作

/*ex:getProperty(myObj,'aze.xyz',0)//安全返回myObj.aze.xyz
*接受属性名称的数组:
*getProperty(myObj,['aze','xyz'],{value:null})
*/
函数getProperty(对象、道具、默认值){
var res,isvoid=function(x){return typeof x==“undefined”| | x==null;}
如果(!isvoid(obj)){
如果(isvoid(props))props=[];
如果(typeof props==“string”)props=props.trim().split(“.”);
if(props.constructor==数组){
res=props.length>1?getProperty(obj[props.shift()],props,defaultValue):obj[props[0]];
}
}
返回typeof res==“未定义”?默认值:res;
}

更新

  • 如果根据ECMAScript 2020或更高版本使用JavaScript,请参阅
  • TypeScript在版本中增加了对可选链接的支持
//像这样使用它
大量的财产

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

一种快速解决方法是在ES6中使用try/catch助手函数:

函数getSafe(fn,defaultVal){ 试一试{ 返回fn(); }捕获(e){ 返回defaultVal; } } //像这样使用它 log(getSafe(()=>obj.a.lot.of.properties)); //或添加可选的默认值
console.log(getSafe(()=>obj.a.lot.of.properties,'nothing');
试试这个。如果
a.b
未定义,它将毫无例外地保留
If
语句

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

在str的回答中,如果属性未定义,将返回值“undefined”,而不是设置的默认值。这有时会导致错误。以下内容将确保在属性或对象未定义时始终返回defaultVal

const temp = {};
console.log(getSafe(()=>temp.prop, '0'));

function getSafe(fn, defaultVal) {
    try {
        if (fn() === undefined || fn() === null) {
            return defaultVal
        } else {
            return fn();
        }
        
    } catch (e) {
        return defaultVal;
    }
}

我以前回答过这个问题,今天碰巧做了一个类似的检查。检查嵌套虚线属性是否存在的简化方法。您可以修改它以返回值,或者使用一些默认值来实现您的目标

function containsProperty(instance, propertyName) {
    // make an array of properties to walk through because propertyName can be nested
    // ex "test.test2.test.test"
    let walkArr = propertyName.indexOf('.') > 0 ? propertyName.split('.') : [propertyName];

    // walk the tree - if any property does not exist then return false
    for (let treeDepth = 0, maxDepth = walkArr.length; treeDepth < maxDepth; treeDepth++) {

        // property does not exist
        if (!Object.prototype.hasOwnProperty.call(instance, walkArr[treeDepth])) {
            return false;
        }

        // does it exist - reassign the leaf
        instance = instance[walkArr[treeDepth]];

    }

    // default
    return true;

}

我通常这样使用:

 var x = object.any ? object.any.a : 'def';

我喜欢曹寿光的回答,但我不喜欢每次调用时都将函数作为参数传递给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);
}
Lodash有一种方法,允许将默认值作为可选的第三个参数,如下所示:

const myObject={
有:"一些",,
缺失:{
瓦尔斯:是的
}
}
常量路径='missing.const.value';
const myValue=551;.get(myObject,path,'default');
console.log(myValue)//打印出上面指定的默认值

如果您使用Babel,您已经可以使用可选的链接语法。这将允许您替换此语法:

console.log(a && a.b && a.b.c);
为此:

console.log(a?.b?.c);
如果有,可以使用它的
.get
方法

获取(a,'b.c.d.e')
或者给它一个默认值

\获取(a'b.c.d.e',默认值)

想象一下,我们想对
x
应用一系列函数,当且仅当
x
为非空时:

if (x !== null) x = a(x);
if (x !== null) x = b(x);
if (x !== null) x = c(x);
现在让我们假设我们需要对
y
执行相同的操作:

if (y !== null) y = a(y);
if (y !== null) y = b(y);
if (y !== null) y = c(y);
z
相同:

if (z !== null) z = a(z);
if (z !== null) z = b(z);
if (z !== null) z = c(z);
正如你所看到的,如果没有一个适当的抽象,我们将一次又一次地重复代码。这样的抽象已经存在了:可能是单子

Maybe monad包含一个值和一个计算上下文:

  • monad保持值的安全性并对其应用函数
  • 计算上下文是应用函数之前的空检查
  • 简单的实现如下所示:

     var x = object.any ? object.any.a : 'def';
    
    ⚠️ 这个实现只是为了说明目的!这不是应该怎么做的,在很多层面上都是错误的。不过,这会让你更好地理解我所说的

    正如你所看到的,没有任何东西可以打破:

  • 我们将一系列函数应用于我们的值
  • 如果在任何时候,值变为null(或未定义),我们就不再应用任何函数
  • const abc=obj=>
    大概
    .of(obj)
    .map(o=>o.a)
    .map(o=>o.b)
    .map(o=>o.c)
    价值
    常量值=[
    {},
    {a:{}},
    {a:{b:{}},
    {a:{b:{c:42}}
    ];
    console.log(
    价值地图(abc)
    );
    
    函数(x){
    this.value=x;//->我们价值的容器
    
    if (z !== null) z = a(z);
    if (z !== null) z = b(z);
    if (z !== null) z = c(z);