如果我有对象的引用:
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 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');
我也遇到了同样的问题,我想看看是否能找到自己的解决方案。这接受要检查的路径作为字符串。
function checkPathForTruthy(obj, path) {
if (/\[[a-zA-Z_]/.test(path)) {
console.log("Cannot resolve variables in property accessors");
return false;
}
path = path.replace(/\[/g, ".");
path = path.replace(/]|'|"/g, "");
path = path.split(".");
var steps = 0;
var lastRef = obj;
var exists = path.every(key => {
var currentItem = lastRef[path[steps]];
if (currentItem) {
lastRef = currentItem;
steps++;
return true;
} else {
return false;
}
});
return exists;
}
下面是一些日志记录和测试用例的片段:
console.clear();var测试案例=[[“data.Messages[0].Code”,true],[“data.Messages[1].Code”,true],[“data.Messages[0]['Code']”,true],['data.Messages[0][“Code”]',true],[“data[Messages][0]['Code']”,错误],[“data['Messages'][0]['Code']”,真]];var path=“data.Messages[0].Code”;变量obj={数据:{消息:[{代码:“0”}, {代码:“1”}]}}函数checkPathForTruthy(obj,路径){if(/\[[a-zA-Z_]/.test(路径)){console.log(“无法解析属性访问器中的变量”);return false;}path=路径替换(/\[/g,“.”);path=路径替换(/]|'|“/g,”“);path=路径拆分(“.”);var步数=0;var lastRef=obj;var logOutput=[];var exists=path.every(key=>{var currentItem=lastRef[path[steps]];if(currentItem){logOutput.push(currentItem);lastRef=当前项;步骤++;返回true;}其他{return false;}});console.log(存在,logOutput);返回存在;}testCase.forEach(testCase=>{如果(checkPathForTruthy(obj,testCase[0])==testCase[1]){console.log(“通过:”+testCase[0]);}其他{console.log(“失败:”+testCase[0]+“预期”+testCase[1]);}});
var a;
a = {
b: {
c: 'd'
}
};
function isset (fn) {
var value;
try {
value = fn();
} catch (e) {
value = undefined;
} finally {
return value !== undefined;
}
};
// ES5
console.log(
isset(function () { return a.b.c; }),
isset(function () { return a.b.c.d.e.f; })
);
如果您在ES6环境中编码(或使用6to5),则可以利用箭头函数语法:
// ES6 using the arrow function
console.log(
isset(() => a.b.c),
isset(() => a.b.c.d.e.f)
);
关于性能,使用try没有性能惩罚。。如果设置了属性,则捕获块。如果属性未设置,则会影响性能。
考虑简单地使用_.has:
var object = { 'a': { 'b': { 'c': 3 } } };
_.has(object, 'a');
// → true
_.has(object, 'a.b.c');
// → true
_.has(object, ['a', 'b', 'c']);
// → true
CMS解决方案工作得很好,但使用/语法可能更方便。我建议如下
var checkNested = function(obj, structure) {
var args = structure.split(".");
for (var i = 0; i < args.length; i++) {
if (!obj || !obj.hasOwnProperty(args[i])) {
return false;
}
obj = obj[args[i]];
}
return true;
};
您可以简单地使用使用点的对象表示法,而不是提供多个参数
var test = {level1:{level2:{level3:'level3'}} };
checkNested(test, 'level1.level2.level3'); // true
checkNested(test, 'level1.level2.foo'); // false
我没有看到任何人使用代理的例子
所以我想出了自己的办法。它的优点是你不必对字符串进行插值。实际上,您可以返回一个可链接的对象函数并用它做一些神奇的事情
函数解析(目标){var noop=()=>{}//我们使用了一个noop函数,因此我们也可以调用方法返回新代理(noop{获取(noop,key){//如果键为_result,则返回最终结果返回键==“_result”? 目标:resolve(//使用目标值或未定义的值进行解析目标==未定义?未定义:目标[key])},//如果我们想测试一个函数,那么由于使用noop,我们可以这么做//而不是在代理中使用target应用(noop,that,args){返回解析(typeof target==“function”?target.apply(that,args):未定义)},})}//接受答案中的一些修改示例var测试={level1:{level2:()=>({level3:'level3'})}}var test1={key1:{key2:['item0‘]}}//您需要在最后获得_result以获得最终结果console.log(解析(测试).level1.level2().level3._result)console.log(解析(测试).level1.level2().level3.level4.level5._result)console.log(解析(test1).key1.key2[0].result)console.log(resolve(test1)[0].key.result)//不存在
以上代码适用于同步内容。但是,如何测试像ajax调用这样的异步功能呢?你是怎么测试的?
fetch('https://httpbin.org/get')
.then(function(response) {
return response.json()
})
.then(function(json) {
console.log(json.headers['User-Agent'])
})
确保可以使用async/await来消除一些回调。但如果你能做得更神奇呢?看起来像这样的东西:
fetch('https://httpbin.org/get').json().headers['User-Agent']
你可能会想知道所有的承诺和链条在哪里。。。这可能会阻碍你所知道的一切。。。但使用相同的代理技术,您实际上可以测试深度嵌套的复杂路径是否存在,而无需编写单个函数
函数解析(目标){返回新代理(()=>{}{获取(noop,key){return key==“then”?target.then.bind(target):解析(承诺.解决(目标).然后(目标=>{if(typeof target[key]==“function”)返回target[key].bind(target)返回目标[键]}))},应用(noop,that,args){返回解析(target.then(result=>{返回result.apply(即,args)}))},})}//这感觉非常同步,但仍然是非阻塞的:)resolve(window)//这将链接一个noop函数,直到您调用then().fetch('https://httpbin.org/get').json().headers[“用户代理”].then(console.log,console.warn)//如果它不存在,则会收到警告//也可以对第一个测试对象使用此方法//也可以,但最后必须调用.then()//另一个例子解析(窗口).fetch('https://httpbin.org/get?items=4&items=2').json().args参数.个项目//很好,您可以在没有准备好的情况下映射数组项.map(n=>~n*4).then(console.log,console.warn)//如果它不存在,则会收到警告