javascript 调用不存在的属性能不报错吗?

问题是这样的 有一个对象
var w = {a:1,b:2,c:{d:123}};

那么我调用
console.log(w.x.y , w.r , w.c.d , w.c.d.xw);

上面的 w.x.y , w.c.d.xw 都会报错 w.r 是 undefined ,我想做要实现的是没有的项用空字符串代替就好了,就像 w.x.y.q.we.r.t.y.u.i.o.......无论多少个属性掉用都不会报错,输出“”就好了, 不用 try catch ,要写全局的,api数据结构比较复习,老是缺少数据,要写的判断好多,所以问问有没有比较好的解决方案.

阅读 16.7k
10 个回答

对象属性的查找是由点运算符(.)引起的,这个运算就负责在引用对象内部查找属性,假如没有找到属性,那么就会返回undefined

形如w.x.y.q.we.r.t.y.u.i.o这样的链式查找,假如说其中一个没有找到,那么返回了undefined,但是接下来还有点运算需要继续查找,然而undefined并不是对象,它不可能有点运算,所以这里会抛出类型错误TypeError


想要实现你的功能,你需要自己封装一个方法,比如这样——

Object.prototype.attr = function (search) {
    const arr = search.split(".");
    let obj = this;
    
    for(let i = 0; i < arr.length; i++) {
        obj = obj[arr[i]];
        if(obj === undefined) {
            return("");
        }
    }
    return(obj);
}

var w = {a:1,b:2,c:{d:123}};
w.attr("x.y.q.we.r.t.y.u.i.o");

代码大概就是这么个意思,我没有测试,思路就是这样,你可以参考参考。

尝试访问undefined变量的属性,必然会报错,所有的程序中都是这样的,这是合理的语法。

你的问题不在于这,而在于如何规范你的开发习惯,甚至是你的API设计。

良好的API设计应该是有明确的格式规范的,所有的可能都是可以预见到的,同样,也不应该有特别深的嵌套。

如果确实有这种恶心的API数据结构,那只能通过优化你的代码结构来让你不至于抓狂,例如你可以通过划分成小的部分来分别处理。

如下数据结构:

{
    x: {
        y: {
            z: {
                m: 111,
                n: 222
            }
        },
        k: {
            q: 333
        }
    }
}

可以这样处理:

function renderX(json) {
    json.y && renderY(json.y);
    json.k && renderK(json.k);
}

function renderY(y) {
    if (!y.z) return null;
    return y.z.m + ':' y.z.n;
}

function renderK(k) {
    return k.q;
}

调用一个不为undefined或null的对象的任意属性都不会报错,没有回返回undefined,但是不能调用undefined或null的属性,会报错,类似java的空指针异常。

感觉你是想实现一个调试用的显示信息的函数,而信息可能不定。
其实你需要自己实现一个 debugShow函数,把console.log(w.x.y , w.r , w.c.d , w.c.d.xw)这样的调用 进行封装,在debugShow中,你可以进行报错处理。例如

function debugShow(...objs){
   for (var v of objs){
   //判断 objs中各个值是否存在的处理
   //对应的log显示处理
   }
}

灵活运用&& 和 ||才是王道

对于ES6以上的环境,你的需求可以用Proxy来实现

var unlimitMemebers = (function() {
    var handler = {
        get: (target, key) => {
            var val = target() && target()[key] || "";
            return new Proxy(() => val, handler);
        },
        apply: (target, thisArg, args) => {
            return target();
        }
    }

    return obj => new Proxy(() => obj, handler);
})();

var w = {a:1,b:2,c:{d:123}};
var ww = unlimitMemebers(w);
console.log(ww.x.y() , ww.r() , ww.c.d() , ww.c.d.xw());

然而受到Proxy只能对object生效的限制,我把基础类型封装了一下,最后必须作为函数调用才能获取值。

要写全局的话, 你可以直接提供一个全局的方法, 然后通过这个方法来访问。try.. catch还是需要用的, 不然只有加判断?

var a = { name: 'Hello' };
var get = function (str) {
  var b = '';
  try {
    b = eval(str);
  } catch (e) {
  }
  return b || '';
}
get('a.name'); // 'Hello'
get('a.a.a.a.a.a.a.a.a'); // ''

lodash的 get方法,可以很简单获取深层的属性而不必层层判断。

_.get(object, path, [defaultValue])

var object = { 'a': [{ 'b': { 'c': 3 } }] };

_.get(object, 'a[0].b.c');
// => 3

_.get(object, ['a', '0', 'b', 'c']);
// => 3

_.get(object, 'a.b.c', 'default');
// => 'default'

提供一个我很久以前写的多级对象属性读写的方法,你可以这样调用:

// 这样调用即可满足你的需要
var w = {a:1,b:2,c:{d:123}};
console.log(attr(w, 'x.y'), attr(w, 'r'), attr(w, 'c.d'), attr(w, 'c.d.xw'));

// 还可以这样使用
var o = {a:{b:[{c:999}]}};
console.log(attr(o, 'a.b[0].c')); // 999
// 可以多级赋值
attr(o, 'a.b[1].c', 888);
console.log(attr(o, 'a.b[1]')); // {c:888}

/**
 * 获取或设置对象属性,允许多级自动创建
 *
 * @param {object} obj 需要属性的对象,通常操作JSON
 * @param {String} name 属性名称(.[]是关键字不可以命名),可以多级,如:name 或 msg.title 或 msg.list[0].user
 * @param value 赋值
 * @return 返回对象属性
 *
 */
function attr(obj, name, value) {
    if (!obj || typeof obj != 'object') return undefined;
    var pAry = name.split('.');
    var key = null;
    var isSet = (arguments.length > 2);
    for (var i = 0; i < pAry.length; i++) {
        key = pAry[i].trim();
        // 如果此键为数组,先要指向数组元素
        if (/^[^\[]+\[\d+\]$/.test(key)) {
            var tempAry = key.split(/[\[\]]/g);
            key = tempAry[0];
            if (!obj[key]) {
                if (!isSet) return undefined;
                obj[key] = [];
            }
            obj = obj[key];
            key = parseInt(tempAry[1]);
        }
        // 然后判断是否最后一级,则直接赋值 结束
        if (i >= pAry.length - 1) {
            if (!isSet) return obj[key];
            obj[key] = value;
            break;
        }
        // 否则还有下级,则指向下级对象
        if (!obj[key] || typeof obj[key] != 'object') {
            if (!isSet) return undefined;
            obj[key] = {};
        }
        obj = obj[key];
    }
    return value;
};
新手上路,请多包涵
const {label='label'} = {key:1, name: 'xxx'}
return label // 'label'

可以试试解构赋值的方式

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题