JavaScript 数组方法与技巧

数组创建、常用方法、遍历、去重、扁平化等操作

问题

掌握 JavaScript 数组的创建方式、常用方法、遍历技巧以及常见操作(去重、扁平化等)。

解答

数组创建

// 字面量
const arr1 = [1, 2, 3];

// 构造函数
const arr2 = new Array(3);        // [empty × 3] 稀疏数组
const arr3 = new Array(1, 2, 3);  // [1, 2, 3]

// Array.of - 解决 new Array 的歧义
const arr4 = Array.of(3);         // [3]
const arr5 = Array.of(1, 2, 3);   // [1, 2, 3]

// Array.from - 从类数组或可迭代对象创建
const arr6 = Array.from('abc');           // ['a', 'b', 'c']
const arr7 = Array.from({ length: 3 }, (_, i) => i); // [0, 1, 2]

改变原数组的方法

const arr = [1, 2, 3];

// push/pop - 尾部操作
arr.push(4);      // 返回新长度 4,arr: [1, 2, 3, 4]
arr.pop();        // 返回 4,arr: [1, 2, 3]

// unshift/shift - 头部操作
arr.unshift(0);   // 返回新长度 4,arr: [0, 1, 2, 3]
arr.shift();      // 返回 0,arr: [1, 2, 3]

// splice - 删除/插入/替换
arr.splice(1, 1);        // 删除索引1的元素,返回 [2],arr: [1, 3]
arr.splice(1, 0, 2);     // 在索引1插入2,arr: [1, 2, 3]
arr.splice(1, 1, 'a');   // 替换索引1,arr: [1, 'a', 3]

// sort - 排序
[3, 1, 2].sort((a, b) => a - b);  // [1, 2, 3] 升序
[3, 1, 2].sort((a, b) => b - a);  // [3, 2, 1] 降序

// reverse - 反转
[1, 2, 3].reverse();  // [3, 2, 1]

// fill - 填充
[1, 2, 3].fill(0);        // [0, 0, 0]
[1, 2, 3].fill(0, 1, 2);  // [1, 0, 3] 填充索引1到2

不改变原数组的方法

const arr = [1, 2, 3];

// concat - 合并
arr.concat([4, 5]);       // [1, 2, 3, 4, 5]

// slice - 截取
arr.slice(1);             // [2, 3]
arr.slice(1, 2);          // [2]
arr.slice(-2);            // [2, 3] 负数从末尾算

// join - 转字符串
arr.join('-');            // '1-2-3'

// indexOf/lastIndexOf - 查找索引
arr.indexOf(2);           // 1
arr.lastIndexOf(2);       // 1

// includes - 是否包含
arr.includes(2);          // true

// find/findIndex - 查找元素
arr.find(x => x > 1);     // 2
arr.findIndex(x => x > 1); // 1

// flat - 扁平化
[1, [2, [3]]].flat();     // [1, 2, [3]]
[1, [2, [3]]].flat(2);    // [1, 2, 3]
[1, [2, [3]]].flat(Infinity); // [1, 2, 3]

遍历方法

const arr = [1, 2, 3];

// forEach - 遍历,无返回值
arr.forEach((item, index) => {
  console.log(item, index);
});

// map - 映射,返回新数组
arr.map(x => x * 2);  // [2, 4, 6]

// filter - 过滤
arr.filter(x => x > 1);  // [2, 3]

// reduce - 累积
arr.reduce((acc, cur) => acc + cur, 0);  // 6

// every - 全部满足
arr.every(x => x > 0);  // true

// some - 部分满足
arr.some(x => x > 2);   // true

数组去重

const arr = [1, 2, 2, 3, 3, 3];

// 方法1: Set
const unique1 = [...new Set(arr)];  // [1, 2, 3]

// 方法2: filter + indexOf
const unique2 = arr.filter((item, index) => arr.indexOf(item) === index);

// 方法3: reduce
const unique3 = arr.reduce((acc, cur) => {
  if (!acc.includes(cur)) acc.push(cur);
  return acc;
}, []);

// 对象数组去重(按某个属性)
const users = [
  { id: 1, name: 'a' },
  { id: 2, name: 'b' },
  { id: 1, name: 'c' }
];

const uniqueUsers = users.filter((item, index, self) => 
  self.findIndex(u => u.id === item.id) === index
);
// [{ id: 1, name: 'a' }, { id: 2, name: 'b' }]

数组扁平化

const arr = [1, [2, [3, [4]]]];

// 方法1: flat
arr.flat(Infinity);  // [1, 2, 3, 4]

// 方法2: 递归
function flatten(arr) {
  return arr.reduce((acc, cur) => {
    return acc.concat(Array.isArray(cur) ? flatten(cur) : cur);
  }, []);
}

// 方法3: 栈
function flattenStack(arr) {
  const stack = [...arr];
  const result = [];
  while (stack.length) {
    const item = stack.pop();
    if (Array.isArray(item)) {
      stack.push(...item);
    } else {
      result.unshift(item);
    }
  }
  return result;
}

类数组转数组

function example() {
  // arguments 是类数组
  const args1 = Array.from(arguments);
  const args2 = [...arguments];
  const args3 = Array.prototype.slice.call(arguments);
}

// NodeList 转数组
const divs = document.querySelectorAll('div');
const divArray = [...divs];

判断数组

Array.isArray([1, 2, 3]);  // true
Array.isArray('abc');      // false

// 其他方式(不推荐)
[1, 2, 3] instanceof Array;  // true,但跨 iframe 会失效
Object.prototype.toString.call([1, 2, 3]) === '[object Array]';  // true

关键点

  • 改变原数组:push/pop/shift/unshift/splice/sort/reverse/fill
  • 不改变原数组:concat/slice/map/filter/reduce/flat
  • 去重首选 [...new Set(arr)],对象数组用 filter + findIndex
  • 扁平化首选 arr.flat(Infinity),兼容性要求高用递归
  • 判断数组用 Array.isArray(),最可靠