如果我有对象的引用:
var test = {};
可能(但不是立即)具有嵌套对象,例如:
{level1: {level2: {level3: "level3"}}};
检查深度嵌套对象中是否存在属性的最佳方法是什么?
警报(测试级别1);生成未定义,但警告(test.level1.level2.level3);失败。
我目前正在做这样的事情:
if(test.level1 && test.level1.level2 && test.level1.level2.level3) {
alert(test.level1.level2.level3);
}
但我想知道是否有更好的方法。
我尝试了递归方法:
function objHasKeys(obj, keys) {
var next = keys.shift();
return obj[next] && (! keys.length || objHasKeys(obj[next], keys));
}
这个keys.length||退出递归,这样它就不会在没有键可供测试的情况下运行函数。测验:
obj = {
path: {
to: {
the: {
goodKey: "hello"
}
}
}
}
console.log(objHasKeys(obj, ['path', 'to', 'the', 'goodKey'])); // true
console.log(objHasKeys(obj, ['path', 'to', 'the', 'badKey'])); // undefined
我正在使用它打印一组具有未知键/值的对象的友好html视图,例如:
var biosName = objHasKeys(myObj, 'MachineInfo:BiosInfo:Name'.split(':'))
? myObj.MachineInfo.BiosInfo.Name
: 'unknown';
我的解决方案,我使用了很长时间(使用字符串不幸,找不到更好的)
function get_if_exist(str){
try{return eval(str)}
catch(e){return undefined}
}
// way to use
if(get_if_exist('test.level1.level2.level3')) {
alert(test.level1.level2.level3);
}
// or simply
alert(get_if_exist('test.level1.level2.level3'));
edit:只有当对象“test”具有全局范围/范围时,这才有效。否则您必须执行以下操作:
// i think it's the most beautiful code I have ever write :p
function get_if_exist(obj){
return arguments.length==1 || (obj[arguments[1]] && get_if_exist.apply(this,[obj[arguments[1]]].concat([].slice.call(arguments,2))));
}
alert(get_if_exist(test,'level1','level2','level3'));
编辑最终版本以允许2种调用方法:
function get_if_exist(obj){
var a=arguments, b=a.callee; // replace a.callee by the function name you choose because callee is depreceate, in this case : get_if_exist
// version 1 calling the version 2
if(a[1] && ~a[1].indexOf('.'))
return b.apply(this,[obj].concat(a[1].split('.')));
// version 2
return a.length==1 ? a[0] : (obj[a[1]] && b.apply(this,[obj[a[1]]].concat([].slice.call(a,2))));
}
// method 1
get_if_exist(test,'level1.level2.level3');
// method 2
get_if_exist(test,'level1','level2','level3');
我知道这个问题很古老,但我想通过将其添加到所有对象来提供一个扩展。我知道人们倾向于不赞成使用Object原型来实现扩展的对象功能,但我发现没有什么比这更容易的了。此外,现在还允许使用Object.defineProperty方法。
Object.defineProperty( Object.prototype, "has", { value: function( needle ) {
var obj = this;
var needles = needle.split( "." );
for( var i = 0; i<needles.length; i++ ) {
if( !obj.hasOwnProperty(needles[i])) {
return false;
}
obj = obj[needles[i]];
}
return true;
}});
现在,为了测试任何对象中的任何属性,只需执行以下操作:
if( obj.has("some.deep.nested.object.somewhere") )
这里有一个jsfiddle来测试它,特别是它包含一些jQuery,如果您直接修改Object.prototype,因为属性变为可枚举,则会中断。这对于第三方图书馆来说应该很好。
我编写了自己的函数,该函数采用所需的路径,并具有一个好的和坏的回调函数。
function checkForPathInObject(object, path, callbackGood, callbackBad){
var pathParts = path.split(".");
var currentObjectPath = object;
// Test every step to see if it exists in object
for(var i=0; i<(pathParts.length); i++){
var currentPathPart = pathParts[i];
if(!currentObjectPath.hasOwnProperty(pathParts[i])){
if(callbackBad){
callbackBad();
}
return false;
} else {
currentObjectPath = currentObjectPath[pathParts[i]];
}
}
// call full path in callback
callbackGood();
}
用法:
var testObject = {
level1:{
level2:{
level3:{
}
}
}
};
checkForPathInObject(testObject, "level1.level2.level3", function(){alert("good!")}, function(){alert("bad!")}); // good
checkForPathInObject(testObject, "level1.level2.level3.levelNotThere", function(){alert("good!")}, function(){alert("bad!")}); //bad