如果我有对象的引用:
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);
}
但我想知道是否有更好的方法。
这是我使用的一个小助手函数,对我来说,它非常简单明了。希望这对一些人有帮助:)。
static issetFromIndices(param, indices, throwException = false) {
var temp = param;
try {
if (!param) {
throw "Parameter is null.";
}
if(!Array.isArray(indices)) {
throw "Indices parameter must be an array.";
}
for (var i = 0; i < indices.length; i++) {
var index = indices[i];
if (typeof temp[index] === "undefined") {
throw "'" + index + "' index is undefined.";
}
temp = temp[index];
}
} catch (e) {
if (throwException) {
throw new Error(e);
} else {
return false;
}
}
return temp;
}
var person = {
hobbies: {
guitar: {
type: "electric"
}
}
};
var indices = ["hobbies", "guitar", "type"];
var throwException = true;
try {
var hobbyGuitarType = issetFromIndices(person, indices, throwException);
console.log("Yay, found index: " + hobbyGuitarType);
} catch(e) {
console.log(e);
}
在html模板中使用一行程序并没有真正好的答案,所以我使用ES6代理做了一个。您只需将一个对象或值传递给“遍历”函数,并使用返回值或回退值的函数调用来完成所需的嵌套调用。使用:
const testObject={深度:{嵌套:{对象:{closure:()=>{return“closure”},数量:9,布尔值:真,数组:[1,2,{foo:{bar:true}}]} }}}遍历(testObject).dep()//{嵌套:{…}}遍历(testObject).non.existint()//未定义的遍历(testObject).dep.enested.obj.closure()//关闭遍历(testObject).dep.enested.obj.array[5]('fallback')//后退,后退遍历(testObject).dep.enested.obj.array[2]()//{foo:{…}}遍历(testObject).dep.enested.obj.array[2].foo.bar()//真的遍历(testObject).dept.netnested.obj.array[2].foo.bar[4]('fallback')//后退,后退遍历(testObject).完全.错误[3].调用().WILL_THROW()//未捕获的TypeError:无法读取未定义的属性“WILL_TROW”
函数本身:
常量遍历=(输入)=>{//唯一空对象const unset=新对象();//我们需要包装器来确保我们可以访问同一个唯一的空对象常量闭包=(输入)=>{//将每个输入包装到此const handler=new Function();handler.input=输入;//返回包装器代理返回新代理(handler{//保持横向移动获取:(target,name)=>{//如果未定义,则作为初始输入提供if(!target.input){返回关闭(未设置);}//否则if(target.input[name]!==未定义){//输入具有该属性返回闭包(target.input[name]);}其他{返回关闭(未设置);}},//带回退的结果apply:(目标,上下文,参数)=>{return handler.input==未设置?args[0]:handler.input;}})}返回闭合(输入);}
这个功能怎么样?它不需要单独列出每个嵌套属性,而是保持“dot”语法(尽管是字符串),使其更具可读性。如果未找到属性,则返回undefined或指定的默认值,如果找到,则返回属性的值。
val(obj, element, default_value)
// Recursively checks whether a property of an object exists. Supports multiple-level nested properties separated with '.' characters.
// obj = the object to test
// element = (string or array) the name of the element to test for. To test for a multi-level nested property, separate properties with '.' characters or pass as array)
// default_value = optional default value to return if the item is not found. Returns undefined if no default_value is specified.
// Returns the element if it exists, or undefined or optional default_value if not found.
// Examples: val(obj1, 'prop1.subprop1.subsubprop2');
// val(obj2, 'p.r.o.p', 'default_value');
{
// If no element is being requested, return obj. (ends recursion - exists)
if (!element || element.length == 0) { return obj; }
// if the element isn't an object, then it can't have properties. (ends recursion - does not exist)
if (typeof obj != 'object') { return default_value; }
// Convert element to array.
if (typeof element == 'string') { element = element.split('.') }; // Split on dot (.)
// Recurse into the list of nested properties:
let first = element.shift();
return val(obj[first], element, default_value);
}
还有一个非常紧凑的:
function ifSet(object, path) {
return path.split('.').reduce((obj, part) => obj && obj[part], object)
}
打电话:
let a = {b:{c:{d:{e:'found!'}}}}
ifSet(a, 'b.c.d.e') == 'found!'
ifSet(a, 'a.a.a.a.a.a') == undefined
它的性能不会很好,因为它拆分了一个字符串(但增加了调用的可读性),并遍历所有内容,即使已经很明显找不到任何内容(但提高了函数本身的可读性。
至少比get快http://jsben.ch/aAtmc