最近是在所在实习公司的第一个sprint,有个朋友又请假了,因此任务比较重,一直这么久都没怎么更新了,这个周末赖了个床,纠结了一下子决定仍是继续写这个系列,虽然比较乏味,可是学到的东西仍是不少的。git
以前主要是针对函数处理部分的API作解读,通过那些天的努力,基本已经解读完了,如今把重点移到数组上。对于数组处理API的解读,从这篇文章开始。github
flatten是一个很基础的函数,在Underscore中也算是一个工具函数,为了方便之后的讲解,今天先阅读flatten函数的源码。面试
首先,咱们带着问题来阅读源码,若是你参加面试,考官让你手写一个展开数组的函数,你会怎么写?数组
咱们接受的参数应该是一个数组,咱们可使用一个叫array的变量表示它,它的返回值应该是一个数组,使用result表示:函数
function flatten(array) {
var result = [];
// ... 展开代码
return result
}
复制代码
而后咱们应该对传入的数组进行类型验证,若是不是数组,咱们应该抛出一个类型异常:工具
function flatten(array) {
var result = [];
if(Object.prototype.toString.call(array) !== '[object Array]')
throw new TypeError('Please pass a array-type object as parameter to flatten function');
else {
// ... 展开代码
}
return result
}
复制代码
这样就能够保证咱们接收到的参数是一个数组,接下来咱们应该遍历array参数,对于它的每一项,若是不是数组,咱们就将其添加到result中,不然继续展开:spa
function flatten(array) {
var result = [];
if(Object.prototype.toString.call(array) !== '[object Array]')
throw new TypeError('Please pass a array-type object as parameter to flatten function');
else {
for(var i = 0; i < array.length; i++) {
if(Object.prototype.toString.call(array[i]) === '[object Array]') {
// ... 继续展开。
}
else {
result.push(array[i]);
}
}
}
return result
}
复制代码
当数组中的项仍是一个数组时,咱们应当如何展开呢? 因为不肯定究竟是嵌套了多少层数组,因此最好是使用递归来展开,可是有新的问题,咱们的flatten函数返回一个数组结果,可是咱们如何把递归结果返回给咱们的result呢,是使用concat方法仍是怎样?prototype
因为函数中对象类型的参数是引用传值,因此咱们能够把result传递给flatten自身,使其直接修改result便可:code
function flatten(array, result) {
var result = result || [];
if(Object.prototype.toString.call(array) !== '[object Array]')
throw new TypeError('Please pass a array-type object as parameter to flatten function');
else {
for(var i = 0; i < array.length; i++) {
if(Object.prototype.toString.call(array[i]) === '[object Array]') {
// ... 递归展开。
arguments.callee(array[i], result);
}
else {
result.push(array[i]);
}
}
}
return result
}
复制代码
以上函数,就基本实现了flatten的功能,再美化一下:对象
var flatten = function(array, result) {
var result = result || [];
var length = array.length;
var toString = Object.prototype.toString;
var type = toString.call(array);
if(type !== '[object Array]')
throw new TypeError('The parameter you passed is not a array');
else {
for(var i = 0; i < length; i++) {
if(toString.call(array[i]) !== '[object Array]') {
result.push(array[i]);
}
else {
arguments.callee(array[i], result);
}
}
}
return result;
}
复制代码
你们能够把上面这段代码拷贝到控制台进行实验。
经过咱们本身亲手实现一个flatten函数,阅读Underscore源码就变得简单了。 下面是Underscore中flatten函数的源码(附注释):
var flatten = function (input, shallow, strict, output) {
output = output || [];
var idx = output.length;
//遍历input参数。
for (var i = 0, length = getLength(input); i < length; i++) {
var value = input[i];
if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
// Flatten current level of array or arguments object.
//若是input数组的元素是数组或者类数组对象,根据是否shallow来展开,若是shallow为true,那么只展开一级。
if (shallow) {
var j = 0, len = value.length;
while (j < len) output[idx++] = value[j++];
} else {
//若是shallow为false,那么递归展开全部层级。
flatten(value, shallow, strict, output);
idx = output.length;
}
} else if (!strict) {
//若是value不是数组或类数组对象,而且strict为false。
//那么直接将value添加到输出数组,不然忽略value。
output[idx++] = value;
}
}
return output;
};
复制代码
Underscore实现的flatten更增强大,它支持类数组对象而不单单是数组,而且它多了两个参数——shallow和strict。
当shallow为true时,flatten只会把输入数组的数组子项展开一级,若是shallow为false,那么会所有展开。
当strict为false时,只要是非数组对象,flatten都会直接添加到output数组中;若是strict为true,那么会无视input数组中的非类数组对象。
更多Underscore源码解读:GitHub