繁体   English   中英

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

[英]test the existence of property in a deep object structure

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

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

这很长。 我猜一定有更优雅的东西。

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

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

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

/*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>=9),因为您需要这样做,以便您的 function 不会出现在枚举中。 有关更多信息: 将方法添加到 Object 原语,但不将其作为属性出现

这是我的代码:

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

沿着这条脉络可能有 3-4 个不同的问题,而答案的数量是原来的四倍。 他们都没有真正让我满意,所以我自己做了,我会分享它。

这个 function 被称为“deepGet”。

例子:

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 httpM/1//xriddlenet

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

正如 mVChr 所说,您当前的解决方案可能与您所能获得的一样好,try..catch 在这里只是懒惰。 它的效率可能要低得多,除了可能更容易输入(但不是很明显)之外,没有什么可推荐的,而且它会更难调试,因为它会默默地隐藏错误。

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

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

但我希望你不会喜欢这样。

如果您有许多这样的深度访问路径,您可能希望创建一个 function 来执行访问并在成功时返回最后一个 object 或在失败时返回一些其他值。 如果失败,您也可以让它返回路径上最后一个非虚假 object。

// 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

我使用这个简单的 function 来玩转 object 的深层属性:

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!

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

   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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM