陪你去看 Lodash.js 起步

时间:2022-11-14 10:05:02

lodash 起步(数组)

Lodash 是一个较为流行的 JavaScript 的实用工具库。

在开发过程中如果能熟练使用一些工具库提供的方法,有利于提高开发效率

笔者从 API 上入手,不分析其源码,先从全局的角度来体验、研究它。分析 API 的命名特性、哪些方法有用,哪些又是比较鸡肋,仅代表笔者个人意见。

Tip:根据Lodash 中文文档得知,API 主要分为:数组、集合、函数、语言、数学、数字、对象、Seq、字符串、实用函数等约10个部分。而数组在我们编程过程中具有非常重要的地位,故笔者决定先从其入手。

环境准备

直接通过引用 bootstrap 免费的 cdn 的方式进行,只需两个文件:index.htmltest-array.js。内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.js"></script>
    <script src="./test-array.js"></script>
</head>
<body>
    
</body>
</html>
let r

// 封装 console.log,使之更剪短
const l = (...ops) => {
    console.log(...ops)
}

{
    r = _.VERSION
    // => 4.17.21
    l(r)
}

{
    r = _.join(['a', 'b', 'c'], '~');
    // => 'a~b~c'
    l(r)
}

Tip: 所有测试都是如上结构

api 设计特性分析

  1. 有的方法可能是考虑兼容性,比如 _.fill,但 javascript 也有对应的原生方法(Array.prototype.fill),倘若使用 babel 库帮忙转换,那这类方法就有些鸡肋
  2. 命名系列化。比如下文的 _.sortedIndex 系列,有 .sortedIndex、.sortedIndexOf、.sortedLastIndexOf、.sortedIndexBy、.sortedLastIndex、.sortedLastIndexBy 共6个,核心是 _.sortedIndex,通常系列方法有功能增强版,例如这里的 _.sortedIndexBy,而命名中有 Last 通常从后往前查找
  3. 系列化的 api 通常有基础版和加强版。例如 _.difference 系列,_.differenceBy_.differenceWith都是加强版
  4. 提供简写进一步提高代码简洁性,比如下面的 _.isEqual
const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
_.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual);
// => [{ 'x': 2, 'y': 1 }]

数组 api 总览

仅列出非鸡肋方法:
Tip: 笔者认为可以用原生方法非常便捷的替换的 api 就是鸡肋。

  • _.chunk - 分块。将数组(array)拆分成多个 size 长度的区块,并将这些区块组成一个新数组
  • _.compact - 压紧。创建一个新数组,包含原数组中所有的非假值元素
  • _.difference 系列 - 。从第一个数组中排除给定数组中的值
  • _.drop 系列 - 减小。例如去除数组前面两个元素
  • _.fromPairs/_.toPair - 一双。例如 [['fred', 30], ['barney', 40]] <---> { 'fred': 30, 'barney': 40 }
  • _.intersection 系列 - 交集
  • _.pull - 移除数组中的值
  • _.sortedIndex - value 应插入已排序数组中的索引
  • _.sortedUniq - 唯一。对已排序的数组过滤重复值
  • _.take - 切片(从左提取 n 个元素)
  • _.union - 并集
  • _.uniq - 唯一(用于去重)
  • _.zip - 压缩[1, 2], [10, 20], [100, 200] -> [[1, 10, 100], [2, 20, 200]]
  • _.unzip 系列 - 类似 _.zip。[[1, 2], [10, 20], [100, 200]] -> [[1, 10, 100], [2, 20, 200]]
  • _.without - 缺乏。可用 array.filter 替代。
  • _.xor 系列 - X集。只存在于某个数组中的值。

数组 api 详细

_.chunk

_.chunk - 分块。

// _.chunk(array, [size=1]) - 将数组(array)拆分成多个 size 长度的区块,并将这些区块组成一个新数组。 如果array 无法被分割成全部等长的区块,那么最后剩余的元素将组成一个区块。
{
    r = _.chunk(['a', 'b', 'c', 'd'], 2)
    // => [['a', 'b'], ['c', 'd']]
    l(r)

    r = _.chunk(['a', 'b', 'c', 'd'], 3)
    // => [['a', 'b', 'c'], ['d']]
    l(r)
}

_.compact

_.compact - 压紧。可用 array.filter 实现

// _.compact(array) - 创建一个新数组,包含原数组中所有的非假值元素。例如false, null,0, "", undefined, 和 NaN 都是被认为是“假值”。
{
    // 返回过滤掉假值的新数组
    // 例如 false, null, 0, "", undefined, NaN 都是被认为是“假值”
    r = _.compact([false, null, 0, "", undefined, NaN, 1]);
    // => [1]
    l(r)

    // 可用 array.filter 实现
    r = [false, null, 0, "", undefined, NaN, 1].filter(v => !!(v))
    // => [1]
    l(r)
}

_.difference 系列

_.difference系列 - 差(从第一个数组中排除给定数组中的值)。_.differenceBy_.differenceWith都是加强版。

// _.difference(array, [values]) - 创建一个具有唯一array值的数组,每个值不包含在其他给定的数组中
// _.differenceBy(array, [values], [iteratee=_.identity]) - 这个方法类似_.difference ,除了它接受一个 iteratee (注:迭代器), 调用array 和 values 中的每个元素以产生比较的标准。
// _.differenceWith(array, [values], [comparator]) - 这个方法类似_.difference ,除了它接受一个 comparator (注:比较器),它调用比较array,values中的元素。 结果值是从第一数组中选择
{

    // 从第一个数组中排除给定数组中的值。
    // 注:非数组会忽略
    r = _.difference([1, 2, 3, 4, 5, 6], [1, 2], [3], [4], 5, [6])
    // 5
    l(r)

    // 与 difference 类似。不同之处是每个值得调用 Math.floor 在比较,比如 2.2 和 2.5 认为是相等的
    r = _.differenceBy([3.1, 2.2, 1.3], [4.4, 2.5], Math.floor);
    // => [3.1, 1.3]
    l(r)

    // The `_.property` iteratee shorthand.
    r = _.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x');
    // => [{ 'x': 2 }]
    l(r)

    var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
    // 与 difference 类似。不同之处是每个值得调用 _.isEqual 这个比较器
    // 与 differenceBy 区别,感觉前者是迭代每个值并从中取得值进行比较,而 differenceWith 直接用比较器直接比较
    r = _.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual);
    // => [{ 'x': 2, 'y': 1 }]
    l(r)
}

_.drop 系列

_.drop系列 - 减小(例如去除数组前面两个元素)。某些场景可以使用 ... 结构语法替换;_.dropRightWhile_.dropWhile 都是增强版。

// _.drop(array, [n=1]) - 创建一个切片数组,去除array前面的n个元素。(n默认值为1。)
// _.dropRight(array, [n=1]) - 创建一个切片数组,去除array尾部的n个元素。(n默认值为1。)
// _.dropRightWhile(array, [predicate=_.identity]) - 创建一个切片数组,去除array中从 predicate 返回假值开始到尾部的部分
// _.dropWhile(array, [predicate=_.identity]) - 创建一个切片数组,去除array中从起点开始到 predicate 返回假值结束部分
{
    // 去除前面两个元素
    r = _.drop([1, 2, 3], 2);
    // => [3]
    l(r);

    // 用...解构语法替代上面例子
    [, , ...r] = [4, 5, 6]
    // => [6]
    l(r)

    // 去除后面两个元素
    r = _.dropRight([1, 2, 3], 2);
    // => [1]
    l(r)

    // 作用难以理解。直接用 filter
    var users = [
        { 'user': 'barney', 'active': true },
        { 'user': 'fred', 'active': false },
        { 'user': 'pebbles', 'active': false }
    ];

    r = _.dropRightWhile(users, function (o) { return !o.active; });
    // => [{ 'user': 'barney', 'active': true }]
    l(r)
}

_.fromPairs & _.toPair

_.fromPairs & _.toPair - 一双。前者可用 array.reduce 实现,后者可用 Object.entries 方便的替代。

// _.fromPairs(pairs) - 与_.toPairs正好相反;这个方法返回一个由键值对pairs构成的对象。
{
    let arr = [['fred', 30], ['barney', 40]]
    r = _.fromPairs(arr);
    // => { 'fred': 30, 'barney': 40 }
    l(r)

    // 用 reduce 重写上述例子
    r = arr.reduce((obj, [key, value]) => {
        obj[key] = value
        return obj
    }, {})
    // => {fred: 30, barney: 40}
    l(r)

    r = _.toPairs(r)
    // => [['fred', 30], ['barney', 40]]
    l(r)

    // 用 Object.entries() 重写上一个示例
    r = { fred: 30, barney: 40 }
    // Object.entries()方法返回一个给定对象自身可枚举属性的键值对数组
    r = Object.entries(r)
    // => [['fred', 30], ['barney', 40]]
    l(r)
}

_.intersection 系列

_.intersection - 交集。_.intersectionBy_.intersectionWith 是增强版。

// _.intersection([arrays]) - 数组的交集
// _.intersectionBy([arrays], [iteratee=_.identity]) - 这个方法类似_.intersection,区别是它接受一个 iteratee 调用每一个arrays的每个值以产生一个值,通过产生的值进行了比较
// _.intersectionWith([arrays], [comparator]) - 这个方法类似_.intersection,区别是它接受一个 comparator 调用比较arrays中的元素。结果值是从第一数组中选择
{
    r = _.intersection([1, 2], [2, 3], [2, 3, 4]);
    // => [2]
    l(r)

    r = _.intersection([1, 2], [3, 4]);
    // => []
    l(r)

    r = _.intersectionBy([2.1, 1.2], [4.3, 2.4], Math.floor);
    // => [2.1]
    l(r)

    const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
    const others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];

    r = _.intersectionWith(objects, others, _.isEqual);
    // => [{ 'x': 1, 'y': 2 }]
    l(r)
}

_.pull 系列

_.pull - 移除数组中的值。_.pullAt 根据索引移除,_.pullAll 系列都是加强版。某些场景可使用 array.filter 替代。

// _.pull(array, [values]) - 移除数组array中所有和给定值相等的元素,使用SameValueZero 进行全等比较
// _.pullAll(array, values) - 这个方法类似_.pull,区别是这个方法接收一个要移除值的数组。
// _.pullAllBy(array, values, [iteratee=_.identity]) - 这个方法类似于_.pullAll ,区别是这个方法接受一个 iteratee(迭代函数) 调用 array 和 values的每个值以产生一个值,通过产生的值进行了比较。
// _.pullAllWith(array, values, [comparator]) - 这个方法类似于_.pullAll,区别是这个方法接受 comparator 调用array中的元素和values比较。
// _.pullAt(array, [indexes]) - 根据索引 indexes,移除array中对应的元素,并返回被移除元素的数组。Tip:删除非连续的多个则不方便用 array.splice 替代
{
    r = [1, 2, 3, 1, 2, 3]
    _.pull(r, 2, 3);
    l(r)
    // => [1, 1]

    // 用 array.filter  重写上一个示例
    r = [1, 2, 3, 1, 2, 3];
    r = r.filter(item => item !== 2 && item !== 3)
    // => [1, 1]
    l(r)

    // 数组中移除 { 'x': 3, 'y': 4 }
    r = [{ 'x': 1, 'y': 2 }, { 'x': 3, 'y': 4 }, { 'x': 5, 'y': 6 }];
    _.pullAllWith(r, [{ 'x': 3, 'y': 4 }], _.isEqual);
    // => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }]
    l(r)

    // 移除索引 1 和 3。
    r = [5, 10, 15, 20];
    _.pullAt(r, 1, 3);
    // => [5, 15]
    l(r)
}

_.sortedIndex 系列

_.sortedIndex - value 应插入已排序数组中的索引。 _.sortedIndexBy 是增强版。_.sortedIndexOf[鸡肋] 和 array.indexOf 类似。

:数组要已排序,否则得不到预期值。

// _.sortedIndex(array, value) - 使用二进制的方式检索来决定 value值 应该插入到数组中 尽可能小的索引位置,以保证array的排序。
// _.sortedIndexOf(array, value) - 这个方法类似_.indexOf,除了它是在已经排序的数组array上执行二进制检索。
// _.sortedLastIndexOf(array, value) - 这个方法类似_.lastIndexOf
// _.sortedIndexBy(array, value, [iteratee=_.identity]) - 这个方法类似_.sortedIndex
// _.sortedLastIndex(array, value) - 此方法类似于_.sortedIndex
// _.sortedLastIndexBy(array, value, [iteratee=_.identity]) - 这个方法类似_.sortedLastIndex
{
    // 数组有序,40 所放索引正确
    r = _.sortedIndex([20, 30, 50], 40)
    // => 2
    l(r)

    // 数组无序,笔者自己都不知道 40 应放哪里
    r = _.sortedIndex([50, 20, 30], 40)
    // => 3
    l(r)

    r = [{ 'x': 4 }, { 'x': 5 }];
    r = _.sortedIndexBy(r, { 'x': 4.5 }, function (o) { return o.x; });
    // => 1
    l(r)

    // 和 array.indexOf 类似
    r = _.sortedIndexOf([4, 5, 5, 5, 6], 5);
    // => 1
    l(r)

    r = [4, 5, 5, 5, 6].indexOf(5)
    // => 1
    l(r)

    // _.sortedLastIndex(array, value) - 和 array.lastIndexOf() 类似
}

_.sortedUniq 系列

_.sortedUniq - 唯一。对已排序的数组过滤重复值,可用 Set 过滤。_.sortedUniqBy 是增强版

// _.sortedUniq(array) - 这个方法类似_.uniq,除了它会优化排序数组。
// _.sortedUniqBy(array, [iteratee]) - 这个方法类似_.uniqBy,除了它会优化排序数组。
{
    r = _.sortedUniq([1, 1, 2]);
    // => [1, 2]
    l(r)

    // 用 Set 过滤重复值
    r = [...new Set([1, 1, 2])]
    // => [1, 2]
    l(r)


    r = _.sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor);
    // => [1.1, 2.3]
    l(r)

    // 利用一个临时数组实现上一个示例
    r = [1.1, 1.2, 2.3, 2.4]
    const tmp = []
    r = r.filter(item => {
        let v = Math.floor(item)
        if (tmp.includes(v)) {
            return false
        }
        tmp.push(v)
        return true
    })
    // => [1.1, 2.3]
    l(r)
}

_.take 系列

_.take - 切片(从左提取 n 个元素)。_.take 和 _.takeRight 可用 array.slice 替代。 _.takeWhile 是增强版。

// _.take(array, [n=1]) - 创建一个数组切片,从array数组的起始元素开始提取n个元素。
// _.takeRight(array, [n=1]) - 创建一个数组切片,从array数组的最后一个元素开始提取n个元素。
// _.takeWhile(array, [predicate=_.identity]) - 与 _.takeRightWhile 类似,只是方向不同
// _.takeRightWhile(array, [predicate=_.identity]) - 从array数组的最后一个元素开始提取元素,直到 predicate 返回假值
{
    r = _.take([1, 2, 3], 2);
    // => [1, 2]
    l(r)

    // 可用 array.slice 重写上一个示例
    r = [1, 2, 3].slice(0, 2)
    // => [1, 2]
    l(r)

    r = _.takeRight([1, 2, 3, 4], 2);
    // => [3, 4]
    l(r)

    // 可用 array.slice 重写上一个示例
    r = [1, 2, 3, 4].slice(-2)
    // => [3, 4]
    l(r)

    const users = [
        { 'user': 'barney', 'active': true },
        { 'user': 'fred', 'active': false },
        { 'user': 'pebbles', 'active': false }
    ];
    r = _.takeRightWhile(users, function (o) { return !o.active; });
    // => objects for ['fred', 'pebbles']
    l(r)

    // 自己实现 _.takeRightWhile
    r = []
    users.reverse().forEach(item => {
        if (item.active) {
            return false
        }
        r.push(item)
    })
    r.reverse()
    // => objects for ['fred', 'pebbles']
    l(r)
}

_.union 系列

_.union - 并集。_.union 可用 Set 方便实现。_.unionBy_.unionWith 是增强版。

// _.union([arrays]) - 创建一个按顺序排列的唯一值的数组。所有给定数组的元素值使用SameValueZero做等值比较。
// _.unionBy([arrays], [iteratee=_.identity]) - 这个方法类似_.union ,除了它接受一个 iteratee (迭代函数),调用每一个数组(array)的每个元素以产生唯一性计算的标准
// _.unionWith([arrays], [comparator]) - 这个方法类似_.union, 除了它接受一个 comparator 调用比较arrays数组的每一个元素。例如比较对象
{
    r = _.union([1, 3, 5, 7, 3], [6, 4, 3]);
    // => [1, 3, 5, 7, 6, 4]
    l(r)

    // 两个数组也可以用 Set 实现
    r = [...new Set([...[1, 3, 5, 7, 3], ...[6, 4, 3]])]
    // or r = Array.from(new Set([...[1, 3, 5, 7], ...[6, 4, 3]]))
    // => [1, 3, 5, 7, 6, 4]
    l(r)

    // 比较处理后的值
    r = _.unionBy([2.1], [1.2, 2.3], Math.floor)
    // => [2.1, 1.2]
    l(r)

    // 比较对象。对象的唯一性
    const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
    const others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];

    r = _.unionWith(objects, others, _.isEqual);
    // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]
    l(r)
}

_.uniq 系列

_.uniq - 唯一(用于去重)。_.uniq 可用 Set 方便实现。_.uniqBy _.uniqWith 是增强版。

// _.uniq(array) - 创建一个去重后的array数组副本。
// _.uniqBy(array, [iteratee=_.identity]) - 这个方法类似_.uniq ,除了它接受一个 iteratee (迭代函数),调用每一个数组(array)的每个元素以产生唯一性计算的标准
// _.uniqWith(array, [comparator]) - 这个方法类似_.uniq, 除了它接受一个 comparator 调用比较arrays数组的每一个元素。例如比较对象
{
    r = _.uniq([2, 1, 2]);
    // => [2, 1]
    l(r)

    r = [...new Set([2, 1, 2])]
    // => [2, 1]
    l(r)

    // 比较处理后的值
    r = _.uniqBy([2.1, 1.2, 2.3], Math.floor);
    // => [2.1, 1.2]
    l(r)

    //  比较对象
    r = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];

    r = _.uniqWith(r, _.isEqual);
    // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]
    l(r)
}

_.zip 系列

_.zip - 压缩。_.zipWith_.zipObject_.zipObjectDeep都是增强版

// _.zip([arrays]) - 创建一个分组元素的数组,数组的第一个元素包含所有给定数组的第一个元素,数组的第二个元素包含所有给定数组的第二个元素,以此类推。
// _.zipWith([arrays], [iteratee=_.identity]) - 这个方法类似于_.zip,不同之处在于它接受一个 iteratee(迭代函数),来指定分组的值应该如何被组合。
// _.zipObject([props=[]], [values=[]]) - 这个方法类似_.fromPairs,除了它接受2个数组,第一个数组中的值作为属性标识符(属性名),第二个数组中的值作为相应的属性值。
// _.zipObjectDeep([props=[]], [values=[]])- 这个方法类似_.zipObject,除了它支持属性路径。
{
    r = _.zip([1, 2], [10, 20], [100, 200])
    // => [[1, 10, 100], [2, 20, 200]]
    l(r)

    // 自己实现一个弱版本
    const zip = (...arrs) => {
        const result = []
        arrs[0].forEach((v, index) => {
            const tmp = []
            arrs.forEach(item => tmp.push(item[index]))
            result.push(tmp)
        })
        return result
    }

    r = zip([1, 2], [10, 20], [100, 200])
    // => [[1, 10, 100], [2, 20, 200]]
    l(r)

    r = _.zipWith([1, 2], [10, 20], [100, 200], function (a, b, c) {
        return a + b + c;
    });
    // => [111, 222]
    l(r)

    // 组合成对象
    r = _.zipObject(['a', 'b'], [1, 2]);
    // => { 'a': 1, 'b': 2 }
    l(r)

    r = _.zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2])
    // => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } }
    l(r)
}

_.unzip 系列

_.unzip - 解压。似于_.zip。_.unzipWith 是增强版。

// _.unzip(array) - 这个方法类似于_.zip,除了它接收分组元素的数组,并且创建一个数组,分组元素到打包前的结构。(:返回数组的第一个元素包含所有的输入数组的第一元素,第一个元素包含了所有的输入数组的第二元素,依此类推。)
// _.unzipWith(array, [iteratee=_.identity]) - 此方法类似于_.unzip,除了它接受一个iteratee指定重组值应该如何被组合。增强版
{
    r = _.unzip([1, 2], [10, 20], [100, 200])
    // => []
    l(r)
    // 用数组包裹后,就和 _.zip 效果相同
    r = _.unzip([[1, 2], [10, 20], [100, 200]])
    // => [[1, 10, 100], [2, 20, 200]]
    l(r)

    r = _.unzipWith([[1, 10, 100], [2, 20, 200]], _.add);
    // => [3, 30, 300]
    l(r)
}

_.without

_.without - 缺乏。可用 array.filter 替代

// _.without(array, [values]) - 创建一个剔除所有给定值的新数组。
{
    r = _.without([2, 1, 2, 3], 1, 2);
    // => [3]
    l(r)

    r = [2, 1, 2, 3].filter(item => ![1, 2].includes(item))
    // => [3]
    l(r)
}

_.xor 系列

_.xor - X集(只存在于某个数组中的值)。_.xorBy_.xorWith 是增强版

// _.xor([arrays]) - 创建一个给定数组唯一值的数组,使用symmetric difference做等值比较。返回值的顺序取决于他们数组的出现顺序。每个元素的差集,即只存在于某个数组中的值,其他数组没有改值。
// _.xorBy([arrays], [iteratee=_.identity]) - 这个方法类似_.xor ,除了它接受 iteratee(迭代器),这个迭代器 调用每一个 arrays(数组)的每一个值,以生成比较的新值。增强版
// _.xorWith([arrays], [comparator]) - 该方法是像_.xor,除了它接受一个 comparator ,以调用比较数组的元素。增强版
{
    r = _.xor([1, 2], [3, 2], [4, 2], [5])
    // => [1, 3, 4, 5]
    l(r)
    r = _.xor([1, 2], [3, 2], [4, 2, 5], [5])
    // => [1, 3, 4]
    l(r)

    // 转换后的值比较
    r = _.xorBy([2.1, 1.2], [2.3, 3.4], Math.floor);
    // => [1.2, 3.4]
    l(r)

    // 对象比较
    const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
    const others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
    r = _.xorWith(objects, others, _.isEqual);
    // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]
    l(r)
}

鸡肋

_.concat

_.concat - 连接数组。与 array.concat 效果相同,也可用 es6+ 语法 ... 实现

// _.concat(array, [values]) - 创建一个新数组,将array与任何数组 或 值连接在一起。
// 
{
    const array = [1];
    r = _.concat(array, 2, [3], [[4]]);

    console.log(r);
    // => [1, 2, 3, [4]]

    // 与原生数组的 concat 效果相同
    r = array.concat(2, [3], [[4]])
    // => [1, 2, 3, [4]]
    l(r)

    r = [...array, 2, ...[3], ...[[4]]]
    // [1, 2, 3, [4]]
    l(r)
}
_.fill

_.fill - 填充。可用 array.fill 替代

// _.fill(array, value, [start=0], [end=array.length]) - 使用 value 值来填充(替换) array,从start位置开始, 到end位置结束(但不包含end位置)。
{

    let array = [1, 2, 3];
    r = [...array]
    _.fill(r, 'a');
    l(r)
    // => ['a', 'a', 'a']

    // array.fill 重写上一个示例
    // fill() 方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。
    r = [...array]
    r.fill('b')
    l(r)
    // =>  ['b', 'b', 'b']

    r = [4, 6, 8, 10]
    _.fill(r, '*', 1, 3)
    l(r)
    // => [4, '*', '*', 10]

    // array.fill 重写上一个示例
    r = [4, 6, 8, 10]
    r.fill('*', 1, 3)
    l(r)
    // => [4, '*', '*', 10]
}

_.findIndex

_.findIndex - 可用 Array.prototype.findIndex 替代

// _.findIndex(array, [predicate=_.identity], [fromIndex=0]) - 该方法类似_.find,区别是该方法返回第一个通过 predicate 判断为真值的元素的索引值(index),而不是元素本身。
// _.findLastIndex(array, [predicate=_.identity], [fromIndex=array.length-1])
{
    let users = [
        { 'user': 'fred', 'active': false },
        { 'user': 'fred', 'active': true },
        { 'user': 'pebbles', 'active': true }
    ];

    r = _.findIndex(users, function (o) { return o.user == 'fred' && o.active === false });
    // => 0
    l(r)

    // array.findIndex 重写上一个示例
    // findIndex()方法返回数组中满足提供的测试函数的第一个元素的索引。若没有找到对应元素则返回 -1。
    r = users.findIndex(o => o.user == 'fred' && o.active === false)
    // => 0
    l(r)

    // The `_.matches` iteratee shorthand.
    r = _.findIndex(users, { 'user': 'fred', 'active': true });
    // => 1
    l(r)
}
_.head

_.head - 可用 arr[index] 或 att.at(index) 替代

// _.head(array) - 获取数组 array 的第一个元素。可用 arr[index] 或 att.at(index) 替代。
{
    const arr = [2, 3, 4]
    r = _.head(arr)
    // => 2
    l(r)

    // 需要 chrome 92+
    // r = arr.at(0)
    // => 2
    // l(r)
}
_.flatten

_.flatten - 变平。array.flat 能非常方便的替代这3个api。

// _.flatten(array) - 减少一级array嵌套深度。
// _.flattenDeep(array) - 将array递归为一维数组。
// _.flattenDepth(array, [depth=1]) - 根据 depth 递归减少 array 的嵌套层级
{
    let arr = [1, [2, [3, [4]], 5]]
    r = _.flatten(arr);
    // => [1, 2, [3, [4]], 5]
    l(r)

    // array.flat 重写上一个示例
    r = arr.flat()
    // => [1, 2, [3, [4]], 5]
    l(r)

    
    r = _.flattenDeep(arr)
    // => [1, 2, 3, 4, 5]
    l(r)

    // 用 array.flat 重写上一个示例
    r = arr.flat(Infinity)
    // => [1, 2, 3, 4, 5]
    l(r)
}
_.remove

_.remove - 删除。可用 array.filter 替代。

// _.remove(array, [predicate=_.identity]) - 移除数组中predicate(断言)返回为真值的所有元素,并返回移除元素组成的数组
{
    let array = [1, 2, 3, 4];
    r = _.remove(array, function (n) {
        return n % 2 == 0;
    });

    // => [2, 4]
    l(r)

    // 用 array.filter 替代
    r = [1, 2, 3, 4]
    r = r.filter(item => item % 2 === 0)
    // => [2, 4]
    l(r)
}
_.tail

_.tail - 尾部。可用 array.slice 替代

// _.tail(array) - 获取除了array数组第一个元素以外的全部元素。
{
    r = _.tail([1, 2, 3]);
    // => [2, 3]
    l(r)

    r = [1, 2, 3].slice(1)
    // => [2, 3]
    l(r)
}
其他
  • _.initial(array) - 获取数组array中除了最后一个元素之外的所有元素。名字不好记。可用 [1, 2, 3].slice(0, -1) 替代 或 let [, ...r] = [1,2,3].reverse(); r.reverse()

  • _.join(array, [separator=',']) - 可用 Array.prototype.join 替代

  • _.last(array) - 获取array中的最后一个元素。 可用 array[array.length - 1] 替代

  • _.lastIndexOf(array, value, [fromIndex=array.length-1]) - 可用 Array.prototype.lastIndexOf 替代

  • _.nth(array, [n=0]) - 获取array数组的第n个元素。直接用数组索引获取 或 Array.prototype.at 替代

  • _.reverse(array) - 逆序。可用 array.reverse 替代。

  • _.slice(array, [start=0], [end=array.length]) - slice。可用 array.slice 替代

  • _.indexOf - 可用 Array.prototype.indexOf 替代