[英]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.