1. 数组基础概念与特性
数组是编程中最基础且重要的数据结构之一,几乎所有编程语言都提供了数组的实现。在JavaScript中,数组是特殊的对象类型,具有以下核心特征:
- 动态大小:JavaScript数组长度可以动态变化,无需预先声明固定大小
- 混合类型:同一个数组中可以存储不同类型的数据(数字、字符串、对象等)
- 零基索引:数组索引从0开始,最后一个元素的索引是length-1
- 浅拷贝:数组复制操作(如slice)创建的是浅拷贝副本
重要提示:虽然JavaScript数组可以存储混合类型,但实践中建议保持数组元素类型一致,这能提高代码可读性和性能。
2. 数组创建与初始化
2.1 创建数组的三种方式
javascript复制// 1. 数组字面量(推荐)
const fruits = ['Apple', 'Banana'];
// 2. Array构造函数
const numbers = new Array(1, 2, 3);
// 3. 从字符串转换
const chars = 'a,b,c'.split(',');
2.2 多维数组创建
JavaScript通过数组嵌套实现多维数组:
javascript复制// 二维数组(矩阵)
const matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
// 三维数组
const cube = [
[[1], [2]],
[[3], [4]]
];
3. 数组核心操作方法
3.1 元素访问与修改
javascript复制const arr = [10, 20, 30];
console.log(arr[1]); // 20
// 安全访问(ES2022)
console.log(arr.at(-1)); // 30(倒数第一个元素)
// 修改元素
arr[2] = 40; // [10, 20, 40]
3.2 长度操作
javascript复制let colors = ['red', 'green'];
colors.length = 5; // 扩展数组,新增元素为empty
console.log(colors); // ['red', 'green', empty × 3]
colors.length = 1; // 截断数组
console.log(colors); // ['red']
4. 数组迭代与遍历
4.1 基本迭代方法
javascript复制const nums = [1, 2, 3];
// for...of循环
for (const num of nums) {
console.log(num);
}
// forEach方法
nums.forEach((num, index) => {
console.log(`${index}: ${num}`);
});
4.2 迭代器方法
javascript复制// 获取键迭代器
const keys = nums.keys();
for (const key of keys) {
console.log(key); // 0, 1, 2
}
// 获取键值对迭代器
const entries = nums.entries();
for (const [index, value] of entries) {
console.log(index, value);
}
5. 数组高阶操作方法
5.1 转换方法
javascript复制// map - 映射新数组
const squares = nums.map(x => x * x); // [1, 4, 9]
// filter - 过滤元素
const evens = nums.filter(x => x % 2 === 0); // [2]
// reduce - 累积计算
const sum = nums.reduce((acc, cur) => acc + cur, 0); // 6
5.2 查找与测试
javascript复制// 查找元素
const found = nums.find(x => x > 1); // 2
// 检查条件
const hasEven = nums.some(x => x % 2 === 0); // true
const allEven = nums.every(x => x % 2 === 0); // false
6. 数组操作实践技巧
6.1 数组去重的多种实现
javascript复制// 方法1:Set + 展开运算符
const unique1 = [...new Set([1,2,2,3])]; // [1,2,3]
// 方法2:filter + indexOf
const unique2 = [1,2,2,3].filter((x, i, arr) => arr.indexOf(x) === i);
// 方法3:reduce
const unique3 = [1,2,2,3].reduce((acc, cur) =>
acc.includes(cur) ? acc : [...acc, cur], []);
6.2 数组排序与乱序
javascript复制// 排序
const sorted = [3,1,2].sort((a,b) => a - b); // [1,2,3]
// 随机乱序(Fisher-Yates算法)
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
7. 数组性能优化建议
- 避免稀疏数组:直接设置length或跳索引创建稀疏数组会影响性能
- 批量操作:使用展开运算符或concat代替多次push
- 循环优化:对于大型数组,for循环通常比forEach更快
- 类型化数组:处理数值数据时,考虑使用TypedArray
javascript复制// 性能对比示例
const bigArray = Array(1000000).fill(0);
console.time('for');
for (let i = 0; i < bigArray.length; i++) {}
console.timeEnd('for'); // ~2ms
console.time('forEach');
bigArray.forEach(() => {});
console.timeEnd('forEach'); // ~10ms
8. 数组与其它数据结构的转换
8.1 数组 ↔ 字符串
javascript复制// 数组转字符串
const str = ['a','b','c'].join('-'); // "a-b-c"
// 字符串转数组
const arr = 'abc'.split(''); // ['a','b','c']
8.2 数组 ↔ 对象
javascript复制// 数组转对象
const obj = Object.fromEntries([
['key1', 'value1'],
['key2', 'value2']
]);
// 对象转数组
const entries = Object.entries({a:1, b:2}); // [['a',1], ['b',2]]
9. 现代JavaScript数组新特性
9.1 ES2023新增方法
javascript复制// 非破坏性方法
const arr = [1, 2, 3];
arr.toReversed(); // [3,2,1](原数组不变)
arr.toSorted((a,b) => b - a); // [3,2,1]
arr.with(1, 99); // [1,99,3]
9.2 数组分组提案(Stage 3)
javascript复制const array = [1, 2, 3, 4, 5];
// 按奇偶分组
const grouped = array.group(x => {
return x % 2 === 0 ? 'even' : 'odd';
});
// { odd: [1, 3, 5], even: [2, 4] }
10. 常见问题与解决方案
10.1 多维数组扁平化
javascript复制const deepArray = [1, [2, [3, [4]], 5]];
// 方法1:flat方法
const flat1 = deepArray.flat(Infinity); // [1,2,3,4,5]
// 方法2:reduce递归
function flatten(arr) {
return arr.reduce((acc, val) =>
Array.isArray(val) ? acc.concat(flatten(val)) : acc.concat(val), []);
}
10.2 数组分块处理
javascript复制function chunk(array, size) {
return Array.from(
{ length: Math.ceil(array.length / size) },
(_, i) => array.slice(i * size, (i + 1) * size)
);
}
chunk([1,2,3,4,5], 2); // [[1,2], [3,4], [5]]
10.3 数组交集/并集/差集
javascript复制// 交集
const intersection = (a, b) => a.filter(x => b.includes(x));
// 并集
const union = (a, b) => [...new Set([...a, ...b])];
// 差集
const difference = (a, b) => a.filter(x => !b.includes(x));
11. 类型化数组与性能优化
当需要处理大量数值数据时,常规数组可能不是最佳选择。JavaScript提供了类型化数组(TypedArray):
javascript复制// 创建包含100万个32位整数的数组
const buffer = new ArrayBuffer(1000000 * 4);
const int32View = new Int32Array(buffer);
// 性能对比
const normalArray = new Array(1000000).fill(0);
const typedArray = new Int32Array(1000000);
console.time('normal');
normalArray.reduce((a, b) => a + b, 0);
console.timeEnd('normal'); // ~15ms
console.time('typed');
typedArray.reduce((a, b) => a + b, 0);
console.timeEnd('typed'); // ~2ms
12. 数组操作的最佳实践
- 优先使用不可变方法:如map、filter代替直接修改原数组
- 合理使用解构:交换变量等操作更简洁
javascript复制let [a, b] = [1, 2]; [a, b] = [b, a]; // 交换变量 - 注意方法复杂度:
- O(1):push/pop
- O(n):shift/unshift/splice
- 大型数组操作:考虑使用Web Worker避免阻塞UI线程
13. 数组在算法中的应用
13.1 双指针技巧
javascript复制// 有序数组的两数之和
function twoSum(nums, target) {
let left = 0, right = nums.length - 1;
while (left < right) {
const sum = nums[left] + nums[right];
if (sum === target) return [left, right];
sum < target ? left++ : right--;
}
return [];
}
13.2 滑动窗口
javascript复制// 最大子数组和
function maxSubArray(nums) {
let max = -Infinity, current = 0;
for (const num of nums) {
current = Math.max(num, current + num);
max = Math.max(max, current);
}
return max;
}
14. Node.js中的Buffer与数组
在Node.js中,Buffer类处理二进制数据,与Uint8Array类似:
javascript复制// Buffer与数组互转
const buf = Buffer.from([1, 2, 3]);
const arr = Uint8Array.from(buf); // [1, 2, 3]
// 高效处理
const bigBuf = Buffer.alloc(1024 * 1024); // 分配1MB内存
15. 浏览器环境下的数组优化
现代浏览器对数组操作有很好的优化,但仍有注意事项:
- 避免频繁修改大型数组:可能导致多次重绘/回流
- 虚拟列表技术:只渲染可见区域的数组元素
- WebAssembly:对性能要求极高的数值计算可考虑
javascript复制// 使用requestAnimationFrame分批处理大型数组
function processLargeArray(array, callback) {
const chunkSize = 1000;
let index = 0;
function doChunk() {
const end = Math.min(index + chunkSize, array.length);
for (; index < end; index++) {
callback(array[index], index, array);
}
if (index < array.length) {
requestAnimationFrame(doChunk);
}
}
doChunk();
}
