近期做了一个项目,在进行DOM渲染时,需要分层渲染,但是拿到的数据是扁平结构,当时草草写了一个方法,实现功能,项目上线后,便想把该方法抽出来,于是有了这篇文章。
先上代码
var transToTreeData = restArguments(function (obj, data, rest) {
if (isArray(obj)) {
rest.unshift(data);
data = obj;
obj = {};
}
if (!data) throw new Error("data是必需项!");
var target = obj || {},
nextKey,
lastKey;
if (rest.length > 1) {
nextKey = rest[rest.length - 1];
lastKey = rest[0];
} else if (rest.length > 0 && rest.length <= 1) {
lastKey = rest[rest.length - 1]
} else {
return data;
}
for (var m = 0; m < data.length; m++) {
if (!!nextKey) {
target[data[m][nextKey]] = target[data[m][nextKey]] || {};
var args = Array(arguments.length);
args[0] = target[data[m][nextKey]];
args[1] = [data[m]];
args[2] = rest.slice(0, rest.length - 1);
transToTreeData.apply(this, args);
} else {
if (!!data[m][lastKey]) {
if (!target[data[m][lastKey]]) {
target[data[m][lastKey]] = [data[m]];
} else {
target[data[m][lastKey]].push(data[m]);
}
} else {
throw new Error("请确认该项包含"+lastKey+"属性!");
}
}
}
return target;
});
// 此处采用underscore中的restArguments方法
function restArguments (func) {
var startIndex = func.length - 1;
return function () {
var length = arguments.length - startIndex,
rest = Array(length),
index = 0;
for (; index < length; index++) {
rest[index] = arguments[index + startIndex];
}
var args = Array(startIndex + 1);
for (index = 0; index < startIndex; index++) {
args[index] = arguments[index];
}
args[startIndex] = rest;
return func.apply(this, args);
}
}
function isArray (arr) {
return Object.prototype.toString.call(arr) === "[object Array]";
}
transToTreeData方法中,data数组是必传项,其他参数都进行了判断处理。在该方法中,树形结构可以无限延续下去,一是使用了递归方法,二是使用了restArguments方法。
递归
编程语言中,函数Func(Type a,……)直接或间接调用函数本身,则该函数称为递归函数。
transToTreeData就是一个递归函数,不停调用自身函数,直到nextKey不存在,则停止调用。
restArgument
ES6中新出的...rest扩展运算符,用来代替arguments,在ES6中,使用...进行标识,在transToTreeData方法中,如果想要不限制参数,实现可延伸树形结构,使用扩展运算符是很好的解决方案。
在写这个方法之前,有学习过underscore的源码,其中就有模拟扩展运算符的实现,于是此处借鉴了其实现原理,模拟实现扩展运算符功能。
运行代码:
var data = [{
name: "xx",
gender: "male",
age: "20",
class: "English"
}, {
name: "aa",
gender: "female",
age: 25,
class: "Mathmatic"
}, {
name: "aa",
gender: "female",
age: 25,
class: "Chinese"
}, {
name: "dd",
gender: "male",
age: 24,
class: "Chinese"
}];
transToTreeData(data, "gender", "class");
运行结果

本文旨在记录与分享,如若有更好的实现方法,欢迎一起讨论,谢谢~