JavaScript 集合操作完全指南 - 从基础到高级实践
2025-07-08 07:41:37作者:裴锟轩Denise
前言
在 JavaScript 开发中,集合操作是最基础也是最重要的技能之一。本文将全面介绍 JavaScript 中数组、Set 和 Map 的各种操作方法,帮助开发者掌握集合处理的精髓。
数组基础操作
数组的创建方式
JavaScript 提供了多种创建数组的方式,每种方式都有其适用场景:
// 字面量方式 - 最简洁的创建方式
const arr1 = [];
// 构造函数方式 - 与字面量等效
const arr2 = new Array();
// 预分配长度的数组 - 创建包含10个空位的数组
const arr3 = new Array(10);
// 带初始值的数组
const arr4 = [1, true, "string"];
// 构造函数方式带初始值
const arr5 = new Array(1, true, "string");
技术要点:
- 字面量方式是日常开发中最常用的方式
- 使用
new Array(10)
创建的是包含10个空位的稀疏数组,不是包含10个undefined的数组 - 数组可以包含任意类型的值,包括混合类型
数组遍历方法
遍历数组是开发中最常见的操作之一,JavaScript 提供了多种遍历方式:
const arr = ['a', 'b', 'c'];
// 传统for循环 - 最基础的方式
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
// for...in循环 - 遍历可枚举属性(包括原型链上的)
for (let index in arr) {
console.log(arr[index]);
}
// for...of循环 - ES6新增,直接获取值
for (let value of arr) {
console.log(value);
}
// forEach方法 - 函数式风格
arr.forEach((val) => console.log(val));
最佳实践建议:
- 纯数组遍历推荐使用
for...of
或forEach
- 需要索引时使用传统
for
循环 - 避免使用
for...in
遍历数组,因为它会遍历所有可枚举属性
数组元素操作
添加/删除元素
const arr = [2, 3];
// 尾部添加单个元素
arr.push(4); // [2, 3, 4]
// 头部添加单个元素
arr.unshift(1); // [1, 2, 3, 4]
// 尾部添加多个元素
arr.push(5, 6); // [1, 2, 3, 4, 5, 6]
// 头部添加多个元素
arr.unshift(-1, 0); // [-1, 0, 1, 2, 3, 4, 5, 6]
// 合并数组
const otherArr = [7, 8];
arr.push(...otherArr); // [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8]
注意事项:
push
/pop
操作在数组尾部进行,时间复杂度为 O(1)unshift
/shift
操作在数组头部进行,需要移动所有元素,时间复杂度为 O(n)- 大数组频繁头部操作应考虑使用其他数据结构
任意位置操作
splice
方法是数组的多功能工具,可以完成插入、删除和替换操作:
const arr = [1, 2, 3, 4, 5];
// 删除操作 - 从索引2开始删除1个元素
arr.splice(2, 1); // 返回 [3],arr变为[1, 2, 4, 5]
// 插入操作 - 在索引2处插入元素
arr.splice(2, 0, 3); // arr恢复为[1, 2, 3, 4, 5]
// 替换操作 - 替换索引1处的元素
arr.splice(1, 1, 'two'); // arr变为[1, 'two', 3, 4, 5]
清空数组的多种方式
let arr = [1, 2, 3];
// 方法1:重新赋值
arr = []; // 最常用方式
// 方法2:设置长度为0
arr.length = 0; // 会立即清空原数组
// 方法3:使用splice
arr.splice(0, arr.length); // 显式清空
// 方法4:循环pop/shift
while(arr.length) arr.pop(); // 性能较差
内存考虑:
- 方法1会创建新数组,原数组如果没有其他引用会被GC回收
- 其他方法会修改原数组,适用于需要保持数组引用的情况
高级数组技巧
数组类型检测
const arr = [1, 2, 3];
// 标准方法
Array.isArray(arr); // true
// 兼容旧环境的方法
Object.prototype.toString.call(arr) === '[object Array]'; // true
// 不可靠的方法
typeof arr; // "object" - 无法区分数组和普通对象
arr instanceof Array; // 跨frame时会失效
数组模拟栈和队列
// 栈实现 (LIFO)
const stack = [];
stack.push(1); // [1]
stack.push(2); // [1, 2]
stack.pop(); // 返回2, stack变为[1]
// 队列实现 (FIFO)
const queue = [];
queue.push(1); // [1]
queue.push(2); // [1, 2]
queue.shift(); // 返回1, queue变为[2]
性能提示:对于频繁队列操作,shift
性能较差,可以考虑使用两个数组模拟队列或使用专门队列库
处理稀疏数组
const arr = [1,,3]; // 稀疏数组
delete arr[0]; // 制造"洞"
// 过滤undefined值
const denseArr = arr.filter(() => true); // [undefined, 3]
Set 和 Map 数据结构
Set 使用示例
Set 是值唯一的集合:
const set = new Set();
set.add(1).add(2).add(1); // 重复添加无效
console.log(set.size); // 2
console.log(set.has(1)); // true
// 数组去重
const uniqueArray = [...new Set([1,2,2,3])]; // [1,2,3]
Map 使用示例
Map 是键值对集合,键可以是任意类型:
const map = new Map();
// 设置各种类型的键
map.set(1, 'number key');
map.set('1', 'string key');
map.set({}, 'object key');
// 获取值
console.log(map.get(1)); // 'number key'
// 大小
console.log(map.size); // 3
Map 与 Object 的区别
- 键类型:Map 键可以是任意值,Object 键只能是字符串或 Symbol
- 顺序:Map 保持插入顺序,Object 的键顺序不保证
- 大小:Map 有 size 属性,Object 需要手动计算
- 性能:Map 在频繁增删场景下性能更好
- 默认属性:Object 有原型链上的默认属性
数组高阶函数实现
实现 filter 方法
Array.prototype.myFilter = function(callback, thisArg) {
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
const result = [];
for (let i = 0; i < this.length; i++) {
if (i in this && callback.call(thisArg, this[i], i, this)) {
result.push(this[i]);
}
}
return result;
};
实现 map 方法
Array.prototype.myMap = function(callback, thisArg) {
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
const result = new Array(this.length);
for (let i = 0; i < this.length; i++) {
if (i in this) {
result[i] = callback.call(thisArg, this[i], i, this);
}
}
return result;
};
实现 reduce 方法
Array.prototype.myReduce = function(callback, initialValue) {
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
const isEmpty = this.length === 0;
let accumulator = initialValue;
let startIndex = 0;
if (isEmpty && arguments.length < 2) {
throw new TypeError('Reduce of empty array with no initial value');
}
if (arguments.length < 2) {
accumulator = this[0];
startIndex = 1;
}
for (let i = startIndex; i < this.length; i++) {
if (i in this) {
accumulator = callback(accumulator, this[i], i, this);
}
}
return accumulator;
};
实用技巧
简化多重条件判断
// 原始方式
if (browser === 'chrome' || browser === 'firefox' || browser === 'safari') {
// ...
}
// 使用数组优化
const supportedBrowsers = ['chrome', 'firefox', 'safari'];
if (supportedBrowsers.includes(browser)) {
// ...
}
二维数组遍历
const matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
// 传统for循环
for (let i = 0; i < matrix.length; i++) {
for (let j = 0; j < matrix[i].length; j++) {
console.log(matrix[i][j]);
}
}
// for...of循环
for (const row of matrix) {
for (const cell of row) {
console.log(cell);
}
}
// 函数式风格
matrix.forEach(row => row.forEach(cell => console.log(cell)));
总结
本文全面介绍了 JavaScript 中集合操作的各种方法,从基础的数组创建、遍历,到高级的 Set/Map 使用,再到数组高阶函数的实现原理。掌握这些集合操作技巧,能够显著提高 JavaScript 开发效率和代码质量。在实际开发中,应根据具体场景选择最合适的集合类型和操作方法,以达到最佳的性能和可维护性。