最近在项目中需要大量操作一些定义好的JSON对象,在访问某个属性时经常要aaa.bbb.ccc来进行访问,这样每次都要判断前面的值是否为空,而且写代码时必须要非常清楚JSON的格式,否则很容易错误。另外更要命的是这样写时代码逻辑和JSON的格式耦合程度非常大,一旦JSON的格式发生了变化,这时改起来的痛苦可想而知。为了减轻负担,我就写了个简单的根据属性来操作JSON对象。
现在的搜索条件支持以下三种格式(搜索的对象满足:)
- “a,b,c” :
具有c属性,且拥有b属性或者是拥有b属性的对象的子节点,b和a的关系以此类推
- “a,>b,c” :
具有c属性,且是拥有b属性的对象的子节点
- “a,b=xx,c” :
具有c属性,且拥有b属性或者是拥有b属性的对象(并且该对象的b属性的值等于xx)的子节点
/**
* 在一个JSON对象中查找某个属性并设置该属性的值
* @param {object} obj JSON对象
* @param {string} name 搜索条件
* @param {boolean=} bRepeat 是否重复查找
* @param value 要重新设置的值
* @returns {*}
*/
function setValueByAttrName(obj,name,bRepeat,value)
{
if(value==undefined) return;
var result= getObjectByAttrName(obj,name,bRepeat);
if(result){
var key=name.split(",");
key=key[key.length-1];
if(key[0]=='>') key=key.substr(1);
if(result instanceof Array) {
for(var i= 0,len=result.length;i<len;++i)
result[i][key]=value;
}
else result[key]=value;
return result;
}
return false;
}
/**
* 在一个JSON对象中查找某个属性并得到包含该属性的对象
* @param {object} obj JSON对象
* @param {string} name 搜索条件
* @param {boolean=} bRepeat 是否重复查找
* @returns {*}
*/
var getObjectByAttrName=(function(){
function _getObjectByAttrName(obj,name,bRepeat,value){
if (!obj || !name) return null;
var result= [] ;
var condition = name.split(",");
var _self = arguments.callee;
if (condition.length === 1) {
var bContinue=true;
var temp = condition[0].split("=");
var name= temp[0];
if(name[0]=='>') name=name.substr(1);
value= value||temp[1];
if (obj[name]!=undefined) {
if (value == undefined || obj[name] == value){
result=result.concat(obj);
bContinue = bRepeat;
}
}
if(bContinue){
if(obj instanceof Array){
for(var i= 0,len=obj.length;i<len;++i){
var data = obj[i];
if (typeof data === 'object') {
var temp = _self.apply(this, [data, name, bRepeat,value]);
if (temp) {
result=result.concat(temp);
if(!bRepeat) break;
}
}
}
}
else if (obj instanceof Object) {
for (var key in obj) {
var data = obj[key];
if (typeof data === 'object') {
var temp = _self.apply(this, [data, name, bRepeat,value]);
if (temp) {
result=result.concat(temp);
if(!bRepeat) break;
}
}
}
}
}
}
else
{
var tempData = [[obj]];
for (var i = 0, len = condition.length; i < len; ++i) {
if(!tempData[i]) break;
for(var j= 0,len2=tempData[i].length;j<len2;++j){
if (result&&result.length&&!bRepeat) break;
var obj=tempData[i][j],key;
key = condition[i];
var temp = _self.apply(this, [obj, key, bRepeat,(i==len-1)?value:undefined]);
if (temp) {
if(i!=len-1) {
tempData[i+1]=[].concat(temp);
for(var k = 0,len3=tempData[i+1].length;k<len3;++k){
var index = condition[i+1][0]=='>'?true:false;
if(index){
if(key[0]=='>') key=key.substr(1);
key=key.split("=")[0];
tempData[i+1][k]=tempData[i+1][k][key];
condition[i+1]=condition[i+1].substr(1);
}
}
}
else result=result.concat(temp);
}
}
}
}
return result?(result.length?(bRepeat?result:result[0]):null):null;
}
return function(obj,name,bRepeat){
return _getObjectByAttrName(obj,name,bRepeat);
}
})();
/**
* 在一个JSON对象中查找某个属性并得到该属性的值
* @param {object} obj JSON对象
* @param {string} name 搜索条件
* @param {boolean=} bRepeat 是否重复搜索
* @returns {*}
*/
function getValueByAttrName(obj,name,bRepeat){
var obj=getObjectByAttrName(obj,name,bRepeat);
if(obj) {
var condition = name.split(",");
var name= condition[condition.length-1];
if(name[0]=='>') name=name.substr(1);
if(bRepeat){
var data=[];
for(var i= 0,len=obj.length;i<len;++i)
data[i]=obj[i][name];
obj=data;
}
else obj=obj[name];
}
return obj;
};
Test:
var data = {
name: "test",
data:{
file: [
{
id: 1000,
attr: {
title: "111",
url: "xxx"
}
},
{
id: 1001,
attr: {
title: "222",
url: "yyy"
}
}
]
}
}
-
getObjectByAttrName(data,”file,url”,true)
output: [Objecttitle: “111”url: “xxx”,Objecttitle: “222”url: “yyy”]
-
getObjectByAttrName(data,”id=1001,url”)
output: Object {title: “222”, url: “yyy”}
另外最近在写代码时又遇到了一个坑,虽然之前也遇到过,但一直没长记性。那就是js的执行环境和作用域问题。js在执行前会有一个预编译过程,在执行每一个函数前都会为这个函数确立一个执行环境,并为这个环境创建一个与之关联的变量对象并将它放在作用域链的顶端。预编译过程将当前作用域内那些用var申明的变量设置为当前活动对象的属性,但是直到真正的赋值前这些变量的值都是undefined,并将那些以function定义的函数也添加为活动对象的属性,而且它们的值正是函数的定义。
for(var i=0;i<2;++i){
setTimeout(function(){
console.log(i);
},1000);
}
for(var i=0;i<2;++i){
var j=i;
setTimeout(function(){
console.log(j);
},1000);
}
这两个函数的输出结果:前面一个输出两个2,后面的输出两个1。