前言
今天是 lodash 的第 6 篇,今天带来的是 flatten,flattenDeep, 和 flattenDepth
Api
_.flatten(array)
Arguments
array(Array) : The array to flatten.
Returns
(Array) : Returns the new flattened array.
Example
_.flatten([1, [2, [3, [4]], 5]]);
// => [1, 2, [3, [4]], 5]
source
function flatten(array) {
var length = array == null ? 0 : array.length;
return length ? baseFlatten(array, 1) : [];
}
核心逻辑只有一个,那就是 baseFlatten
baseFlatten
flatten 和 flattenDeep, flattenDepth 的核心方法都是 baseFlatten
function baseFlatten(array, depth, predicate,result) {
var index = -1,
length = array.length;
predicate || (predicate = isFlattenable);
result || (result = []);
while (++index < length) {
var value = array[index];
// 判断深度 和 value 是否可以再次展开
if (depth > 0 && predicate(value)) {
if (depth > 1) {
// 可以 展开,并且 depth > 1,需要深度递归value
baseFlatten(value, depth - 1, predicate,result);
} else {
// depth = 1,说明value是这种情况
// value = [1,2,3]
arrayPush(result, value);
}
} else {
// 不能展开 或者 depth == 0,直接赋值
result[result.length] = value;
}
}
return result;
}
baseFlatten 需要四个参数,分别是 原数组 array,要拍平的深度 depth,断言 predicate, 结果 result
重点在于这个 while 循环,如果 是 depth > 0 并且 数组中的每一项元素满足 predicate,那么继续判断是否是 depth 是否是 大于 1,如果 大于 1,则需要继续递归,如果不是,则使用 arrayPush,
如果不满足上述条件,则直接把 value 放到 result 的最后
graph TD
value[/array的每一个元素value/]
满足条件1{depth>0 && predicate}
满足条件2{depth>1}
Start --> value
value --> 满足条件1
满足条件1 --是-->满足条件2
满足条件2--是-->递归执行baseFlatten
满足条件2--否-->arrayPush
满足条件1 --否-->添加到result的尾部
predicate很容易想到,就是判断判断元素是否是一个数组
predicate 默认值是 isFlattenable;
来看看这个 isFlattenable
isFlattenable
var spreadableSymbol = Symbol.isConcatSpreadable;
function isFlattenable(value) {
return Array.isArray(value) ||
!!(spreadableSymbol && value && value[spreadableSymbol]);
}
这个 Symbol.isConcatSpreadable 是什么意思?
这个属性代表了 元素是否可以使用 concat,看下文例子
const alpha = ['a', 'b', 'c'];
const numeric = [1, 2, 3];
let alphaNumeric = alpha.concat(numeric);
console.log(alphaNumeric);
// Expected output: Array ["a", "b", "c", 1, 2, 3]
numeric[Symbol.isConcatSpreadable] = false;
alphaNumeric = alpha.concat(numeric);
console.log(alphaNumeric);
// Expected output: Array ["a", "b", "c", Array [1, 2, 3]]
对于类数组 (array-like) 对象,默认不展开。期望展开其元素用于连接,需要设置 Symbol.isConcatSpreadable 为 true
var x = [1, 2, 3];
var fakeArray = {
[Symbol.isConcatSpreadable]: true,
length: 2,
0: "hello",
1: "world"
}
x.concat(fakeArray); // [1, 2, 3, "hello", "world"]
这个属性如果是 false ,那就是使用 concat 时不展开,元编程的一种;
arrayPush
把 values 数组中的元素 一个一个 的放入到 array 的尾部
function arrayPush(array, values) {
var index = -1,
length = values.length,
offset = array.length;
while (++index < length) {
array[offset + index] = values[index];
}
return array;
}
举一个例子
let arr = [1,2,3];
let arr2 = [4,5,6,7];
// 那么
此时 length 是 4
offset 是 3
arr[3 + 0] = arr2[0];
arr[3 + 1] = arr2[1];
arr[3 + 2] = arr2[2];
_.flattenDeep(array)
Example
_.flattenDeep([1, [2, [3, [4]], 5]]);
// => [1, 2, 3, 4, 5]
也就是说,把所有的深度数组都拍平
function flattenDeep(array) {
var length = array == null ? 0 : array.length;
return length ? baseFlatten(array, Infinity) : [];
}
只需要把 baseFlatten 中的 depth 设置为 Infinity 即可
那么明白这个道理,
flattenDepth(array, [depth=1]) 就是在传入 baseFltten 传入 depth
flattenDepth(array, [depth=1])
var array = [1, [2, [3, [4]], 5]];
_.flattenDepth(array, 1);
// => [1, 2, [3, [4]], 5]
_.flattenDepth(array, 2);
// => [1, 2, 3, [4], 5]
flattenDepth 中也就是把 depth 格式化然后传入 baseFlatten
function flattenDepth(array, depth) {
var length = array == null ? 0 : array.length;
if (!length) {
return [];
}
depth = depth === undefined ? 1 : Math.floor(depth);
return baseFlatten(array, depth);
}
结尾
这个 baseFlatten 也是一个基础方法,在 lodash 中有很多基础方法,只需要传入不同的参数,然后传入,就可以得到不同的效果,这样的方式很符合函数式编程的思想