Skip to content

数组有哪些必须会的方法? ​

会改原数组的方法要和返回新数组的方法分开记。面试里最常写的是 map、filter、reduce,以及怎么按条件去重。

原理 ​

先看会不会改原数组,再看返回值是新数组、一个元素,还是一个累计值。回调的参数一般是 (当前值, 下标, 原数组)。

不改原数组 ​

  • map(fn):每一项都经过 fn,返回同样长度的新数组。某一项没有返回值时,新数组对应位置是 undefined。
js
[1, 2, 3].map((n) => n * 2) // [2, 4, 6]
[1, 2].map((n) => {
  if (n > 1) return n
}) // [undefined, 2]
  • filter(fn):只留下 fn 返回真值的项,返回新数组。长度可能变短。
js
[1, 2, 3, 4].filter((n) => n % 2 === 0) // [2, 4]
  • reduce(fn, 初始值):从左到右收成一个值。fn 的第一个参数是上一次的累计值。不传初始值时,第一项当累计值,空数组会抛错。
js
[1, 2, 3].reduce((total, n) => total + n, 0) // 6
[2, 3].reduce((total, n) => total * n) // 6,没有初始值,从 2 开始乘
  • slice(start, end):复制从 start 到 end(不含 end)的一段。负数从末尾数。不传参数等于浅拷贝整个数组。
js
const list = [1, 2, 3, 4]
list.slice(1, 3) // [2, 3]
list.slice(-2) // [3, 4]
list.slice() // [1, 2, 3, 4],list 本身不变
  • concat(...items):把当前数组和参数拼成新数组。参数里的数组只展平一层。
js
[1, 2].concat(3, [4, [5]]) // [1, 2, 3, 4, [5]]
  • flat(depth):把嵌套数组展平,默认一层。flat(Infinity) 全部展平。
js
[1, [2, [3]]].flat() // [1, 2, [3]]
[1, [2, [3]]].flat(Infinity) // [1, 2, 3]
  • flatMap(fn):先对每一项做 map,再把结果展平一层。fn 返回数组时,这些元素会接到新数组里,而不是再包一层。
js
[1, 2].flatMap((n) => [n, n * 10]) // [1, 10, 2, 20]

原始值去重可以用 Set,它同样不改原数组:

js
const unique = (list) => [...new Set(list)]
unique([1, 1, 2]) // [1, 2]

会改原数组 ​

  • push(...items):末尾追加,返回新的 length。
js
const list = [1]
list.push(2, 3) // 3
list // [1, 2, 3]
  • pop():删掉最后一项并返回它。空数组返回 undefined。
js
const list = [1, 2, 3]
list.pop() // 3
list // [1, 2]
  • unshift(...items):头部插入,返回新的 length。
js
const list = [2, 3]
list.unshift(1) // 3
list // [1, 2, 3]
  • shift():删掉第一项并返回它。后面的项都要前移。
js
const list = [1, 2, 3]
list.shift() // 1
list // [2, 3]
  • splice(start, deleteCount, ...items):从 start 删掉 deleteCount 项,并可在同一位置插入 items。返回被删掉的数组,不是剩下的原数组。
js
const list = [1, 2, 3, 4]
list.splice(1, 2, 9) // [2, 3]
list // [1, 9, 4]
  • sort(compare):原地排序。不传比较函数时按字符串排,数字要写 (a, b) => a - b。返回排好的原数组。
js
const byText = [10, 2, 1]
byText.sort() // [1, 10, 2],按字符串比较

const byNumber = [10, 2, 1]
byNumber.sort((a, b) => a - b) // [1, 2, 10]
  • reverse():原地倒序,返回原数组。
js
const list = [1, 2, 3]
list.reverse() // [3, 2, 1]
  • fill(value, start = 0, end = arr.length):用同一个值填满一段,返回原数组。如果 value 是对象,每一格都是这个对象的同一引用。
js
[1, 2, 3].fill(0, 1) // [1, 0, 0]
const box = {}
const filled = Array(2).fill(box)
filled[0] === filled[1] // true

查找和判断 ​

  • forEach(fn):逐项执行,返回 undefined。不能 break,也不会把返回值收成数组。
js
const seen = []
[1, 2].forEach((n) => seen.push(n))
seen // [1, 2]
  • find(fn):返回第一个让 fn 为真的元素,没有则是 undefined。
js
[{ id: 1 }, { id: 2 }].find((item) => item.id === 2) // { id: 2 }
[1, 2].find((n) => n > 9) // undefined
  • findIndex(fn):返回这个元素的下标,没有则是 -1。
js
[10, 20, 30].findIndex((n) => n > 15) // 1
  • indexOf(value):用严格相等找下标,找不到是 -1。NaN 永远找不到。
js
[1, 2, 2].indexOf(2) // 1
[NaN].indexOf(NaN) // -1
  • includes(value):是否包含该值,能认出 NaN。
js
[1, 2].includes(2) // true
[NaN].includes(NaN) // true
  • some(fn):有一项为真就返回 true,并停下来。
js
[1, 2, 3].some((n) => n > 2) // true
[].some(() => true) // false
  • every(fn):全部为真才返回 true。空数组上 every 为真,some 为假。
js
[1, 2, 3].every((n) => n > 0) // true
[].every(() => false) // true

追问 ​

forEach 和 map 怎么选?

只做副作用用 forEach,它的返回值是 undefined。要得到新数组用 map。两者都不能用 break 中断,要中断用 for 或 some。

稀疏数组会怎样?

map、forEach 会跳过空位。Array(3) 有三个空位,map 之后仍然是空位,不是 undefined 被映射过。

易错点 ​

  • sort 会改原数组,而且默认不是按数字大小。
  • splice 返回被删掉的元素,不是剩下的数组。
  • 对象数组去重不能靠 Set,除非先规定相等规则。
js
// 根据唯一 key(id),**保留第一次出现的对象**  时间复杂度 O (n),速度快,最常用
function uniqueByKey(arr, key) {
    const map = new Map();
    return arr.filter(item => {
        if(map.has(item[key])) {
            return false;
        }
        map.set(item[key], true);
        return true;
    });
}
// 保留最后一次出现
function uniqueByKeyLast(arr, key) {
    let map = new Map();
    for(let item of arr) {
        map.set(item[key], item);
    }
    return Array.from(map.values());
}
// some 循环,大数据性能差
function uniqueByReduce(arr, key) {
    return arr.reduce((acc, curr) => {
        if(!acc.some(item => item[key] === curr[key])) {
            acc.push(curr);
        }
        return acc;
    },[]);
}

相关 ​