测试深层对象结构中属性的存在

时间:2011-06-01 22:35:11

标签: javascript

在javascript中,假设我想访问对象深处的属性,例如:

entry.mediaGroup [0] .contents [0] .URL

在该结构的任何位置,属性可能未定义(因此可能未设置mediaGroup)。

简单的说法是什么:

if( entry.mediaGroup[0].contents[0].url ){
   console.log( entry.mediaGroup[0].contents[0].url )
}

没有产生错误?如果未定义任何一点,这种方式将产生一个未定义的错误。

我的解决方案

if(entry) && (entry.mediaGroup) && (entry.MediaGroup[0]) ...snip...){
   console.log(entry.mediaGroup[0].contents[0].url)
}

非常冗长。我猜想必须有更优雅的东西。

6 个答案:

答案 0 :(得分:7)

这是一种非常懒惰的方法,但它符合许多类似情况的标准:

try {
  console.log(entry.mediaGroup[0].contents[0].url);
} catch (e) {}

这不应该在可能会忽略其他错误的长代码块上完成,但应该适用于这样的简单情况。

答案 1 :(得分:3)

/*decend through an object tree to a specified node, and return it.
  If node is unreachable, return undefined. This should also work with arrays in the tree.                                                                                               
  Examples:                                                                                                                                                                            
    var test1 = {a:{b:{c:{d:1}}}};                                                                                                                                            
    console.log(objectDesend(test1, 'a', 'b', 'c', 'd'));                                                                                                                
    var test2 = {a:{b:{c:1}}};     //will fail to reach d                                                                                                                                         
    console.log(objectDesend(test2, 'a', 'b', 'c', 'd'));
*/
var objectDescend = function(){
    var obj = arguments[0];
    var keys = arguments;
    var cur = obj;                                                                                                                                                        
    for(var i=1; i<keys.length; i++){                                                                                                                                     
        var key = keys[i];                                                                                                                                                
        var cur = cur[key];                                                                                                                                               
        if(typeof(cur)=='undefined')                                                                                                                                      
            return cur;                                                                                                                                                   
    }                                                                                                                                                                     
    return cur;                                                                                                                                                           
}                                                                                                                                                                         

var test1 = {a:{b:{c:{d:1}}}};                                                                                                                                            
console.log(objectDescend(test1, 'a', 'b', 'c', 'd'));                                                                                                                
var test2 = {a:{b:{c:1}}};                                                                                                                                              
console.log(objectDescend(test2, 'a', 'b', 'c', 'd'));

因此,这将返回您要查找的值,或者由于该值不存在而未定义。它不会返回false,因为它实际上可能是您正在寻找的值(d:false)。

在我的代码库中,我添加了Object.prototype.descend,因此我可以执行test1.descend('a','b','c','d')。这只适用于ECMAScript 5(IE&gt; = 9),因为你需要这样做才能使你的函数不出现在枚举中。欲了解更多信息: Add a method to Object primative, but not have it come up as a property

这是我的代码:

Object.defineProperty(Object.prototype, 'descend', {
    value: function(){
        var keys = arguments;
        var cur = this;
        for(var i=0; i<keys.length; i++){
            var key = keys[i];
            var cur = cur[key];
            if(typeof(cur)=='undefined')
                return cur;
        }
        return cur;
    }
});



var test1 = {a:{b:{c:{d:false}}}};
//this will return false, which is the value of d                                                                                   
console.log(test1.descend('a', 'b', 'c', 'd'));                                                                                                                       
var test2 = {a:{b:{c:1}}};
//undefined since we can't reach d.                                                                                                
console.log(test2.descend(test2, 'a', 'b', 'c', 'd'));

答案 2 :(得分:2)

你当前的解决方案可能和你能得到的一样好,正如mVChr所说,try..catch在这里很懒。除了可能更容易键入(但不是那么明显)之外,它可能远没有那么高效,也没有什么可推荐的,而且它会更难调试,因为它会默默地隐藏错误。

真正的问题是通过尝试此类访问而创建的非常长的“参考蠕虫”。至少减少属性查找次数的原始替代方法是:

var o;
if ( (o = entry       ) &&
     (o = o.mediaGroup) &&
     (o = o[0]        ) &&
     (o = o.contents  ) &&
     (o = o[0]        )) {
  alert(o.url);
}

但我希望你不会那样。

如果您有许多此类深层访问路径,您可能希望创建一个函数来执行访问,并在成功时返回最后一个对象,或者在失败时返回其他一些vaule。如果失败,您还可以让它返回路径上的最后一个非假名对象。

// Create test object
var entry = {};
entry.mediaGroup = [{
  contents: [{url: 'url'}]
}];

// Check that it "works" 
// alert(entry.mediaGroup[0].contents[0].url);


// Deep property access function, returns last object
// or false
function deepAccess(obj) {

  var path = arguments;
  var i = 0, iLen = path.length;
  var o = path[i++];  // o is first arg
  var p = path[i++];  // p is second arg

  // Go along path until o[p] is falsey
  while (o[p]) {
    o = o[p];
    p = path[i++];
  }

  // Return false if didn't get all the way along
  // the path or the last non-falsey value referenced
  return (--i == iLen) && o;
}

// Test it    
var x = deepAccess(entry, 'mediaGroup','0','contents','0');
alert(x && x.url);  // url

var x = deepAccess(entry, 'mediaGroup','1','contents','0');
alert(x && x.url);  // false

答案 3 :(得分:2)

这种情况下可能存在3-4个不同的问题,答案数量是答案的4倍。他们都没有真正满足我,所以我自己做了,我会分享它。

此功能被称为&#34; deepGet&#34;。

示例:

deepGet(mySampleData, "foo.bar[2].baz", null);

以下是完整代码:

function deepGet (obj, path, defaultValue) {

    // Split the path into components
    var a = path.split('.');

    // If we have just one component left, note that for later.
    var last = (a.length) === 1;

    // See if the next item is an array with an index
    var myregexp = /([a-zA-Z]+)(\[(\d+)\])+/; // matches:  item[0]
    var match = myregexp.exec(a[0]);

    // Get the next item
    var next;
    if (match !== null) {
        next = obj[match[1]];
        if (next !== undefined) {
            next = next[match[3]];
        }
    } else {
        next = obj[a[0]];
    }

    if (next === undefined || next === null) {
        // If we don't have what we want, return the default value
        return defaultValue;
    } else {
        if (last) {
            // If it's the last item in the path, return it
            return next; 
        } else { 
            // If we have more items in the path to go, recurse
            return deepGet (next, a.slice(1).join("."), defaultValue); 
        }
    }
}

这是一个jsFiddle:http://jsfiddle.net/7quzmjh8/2/

我受到这两件事的启发:

http://designpepper.com/blog/drips/making-deep-property-access-safe-in-javascript.html http://jsfiddle.net/wxrzM/1/

希望这对那里的人有用:)

答案 4 :(得分:0)

我使用这个简单的函数来玩深层对象属性:

getProperty = function(path) {
    try {
        return eval(path);
    }
    catch (e) {
        return undefined;
    }
};

以下是一个例子:

var test = {a:{b:{c:"success!"}}};

alert(getProperty('test.c.c'));
// undefined

alert(getProperty('test.a.b.c'));
// success!

答案 5 :(得分:-1)

这是我已经使用了一段时间

   var obj = { a: { b: [
                        { c: {d: 'XYZ'} }
                    ] } };

    // working

    obj.a.b[0].c.d = null;
    console.log('value:'+getProperty(obj, 'a.b[0].c.d', 'NOT-AVAILABLE')); // value:null

    obj.a.b[0].c.d = 'XYZ';
    console.log('value:'+getProperty(obj, 'a.b[0].c.d', 'NOT-AVAILABLE')); // value:XYZ
    console.log('value:'+getProperty(obj, 'a.b[0].c.d.k.sds', 'NOT-AVAILABLE')); // value:NOT-AVAILABLE

    obj.a.b[0].c = null;
    console.log('value:'+getProperty(obj, 'a.b[0].c.d', 'NOT-AVAILABLE'));  // value:NOT-AVAILABLE


    // will not work
    //console.log('v:'+getProperty(obj, 'a.b["0"].c.d'));

这是函数

function getProperty(obj, str, defaultValue){

    var props = str.split('.').map(function(prop){
        var arrAccessRegEx = /(.*)\[(.*)\]/g;
        if (arrAccessRegEx.test(prop)){
            return prop.split(arrAccessRegEx).filter(function(ele){return ele!=''; });
        } else {
            var retArr = [];
            retArr.push(prop);
            return retArr
        };
    });

    //console.log(props);

    for(var i=0;i<props.length;i++){
        var prop = props[i][0];

        //console.log('prop:'+prop);

        if (obj === null) return defaultValue;

        obj = obj[prop];

        if (obj === undefined) return defaultValue;

        if (props[i].length == 2){
            var idx = props[i][1];
            if (!(obj instanceof Array)) return defaultValue;
            if (idx < obj.length ){
                obj = obj[idx];
                if (obj === undefined) return defaultValue;
            }
        }

    } // for each item in split

    return obj;
}