1. JavaScript数组基础与核心特性
JavaScript数组是前端开发中最基础也最常用的数据结构之一。与其它编程语言不同,JS数组本质上是特殊类型的对象,这带来了许多独特的特性和用法。我们先从最基础的数组创建开始:
javascript复制// 数组字面量创建
const fruits = ['apple', 'banana', 'orange'];
// Array构造函数创建
const numbers = new Array(1, 2, 3);
// ES6 Array.of方法创建
const digits = Array.of(7, 8, 9);
数组的索引特性是其最直观的特征。JS数组使用从0开始的数字索引,但有趣的是,数组的length属性并不是只读的:
javascript复制const arr = [1, 2, 3];
arr.length = 5; // 数组扩展为[1, 2, 3, empty × 2]
arr.length = 2; // 数组截断为[1, 2]
数组与普通对象最大的区别在于它自动维护的length属性和一系列内置方法。但本质上,JS数组仍然是对象,这意味着:
javascript复制const arr = [];
arr['someKey'] = 'value'; // 可以像普通对象一样添加属性
console.log(arr.someKey); // 输出"value"
注意:虽然技术上可行,但混合使用数组索引和对象属性通常不是一个好习惯,这会导致代码难以理解和维护。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数组的静态方法与实例方法
2.1 Array构造函数的静态方法
Array构造函数本身提供了一些有用的静态方法:
Array.isArray():判断一个值是否为数组Array.from():从类数组对象或可迭代对象创建新数组Array.of():根据参数创建新数组
javascript复制// 判断数组
console.log(Array.isArray([1, 2])); // true
console.log(Array.isArray({})); // false
// 从类数组对象创建
const nodeList = document.querySelectorAll('div');
const divArray = Array.from(nodeList);
// Array.of与构造函数的区别
console.log(Array.of(7)); // [7]
console.log(new Array(7)); // [empty × 7]
2.2 数组实例的核心方法
数组实例提供了丰富的方法,可以分为几大类:
修改原数组的方法:
push()/pop():在数组末尾添加/移除元素unshift()/shift():在数组开头添加/移除元素splice():在指定位置添加/删除元素sort()/reverse():排序和反转数组
不修改原数组的方法:
concat():合并数组slice():提取数组片段join():将数组转为字符串
迭代方法:
forEach():遍历数组map():映射新数组filter():过滤数组reduce()/reduceRight():累积计算
javascript复制// splice示例
const months = ['Jan', 'March', 'April'];
months.splice(1, 0, 'Feb'); // 在索引1处插入'Feb'
// 结果: ['Jan', 'Feb', 'March', 'April']
// reduce示例
const sum = [1, 2, 3].reduce((acc, cur) => acc + cur, 0);
console.log(sum); // 6
3. 数组的迭代与查找方法
现代JavaScript提供了多种强大的数组迭代方法,每种方法都有其特定的使用场景。
3.1 基本迭代方法
forEach()是最简单的迭代方法,但它无法中途跳出循环:
javascript复制const arr = [1, 2, 3];
arr.forEach(item => {
console.log(item);
if (item === 2) return; // 这只是退出当前回调,不是整个循环
});
map()方法创建一个新数组,其结果是原数组每个元素调用回调函数的返回值:
javascript复制const doubled = [1, 2, 3].map(x => x * 2);
console.log(doubled); // [2, 4, 6]
提示:当你不关心返回值时使用
forEach(),当你需要转换数组时使用map()。
3.2 查找与测试方法
find()/findIndex():查找符合条件的第一个元素/索引includes():检查是否包含某个值some()/every():测试是否有/所有元素满足条件
javascript复制const users = [
{id: 1, name: 'Alice'},
{id: 2, name: 'Bob'},
{id: 3, name: 'Charlie'}
];
const bob = users.find(user => user.name === 'Bob');
console.log(bob); // {id: 2, name: 'Bob'}
const hasAdmin = users.some(user => user.role === 'admin');
console.log(hasAdmin); // false
3.3 性能考量
不同的迭代方法有不同的性能特点:
for循环:通常最快,但代码冗长forEach:比for慢但更简洁map/filter:创建新数组有额外开销reduce:适合复杂聚合操作
javascript复制// 性能对比
const largeArray = Array(1000000).fill(1);
console.time('for');
for (let i = 0; i < largeArray.length; i++) {}
console.timeEnd('for');
console.time('forEach');
largeArray.forEach(() => {});
console.timeEnd('forEach');
4. 数组的高级特性与ES6+新增功能
4.1 扩展运算符与解构
ES6引入的扩展运算符(...)极大简化了数组操作:
javascript复制// 合并数组
const arr1 = [1, 2];
const arr2 = [3, 4];
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4]
// 函数参数
function sum(a, b, c) { return a + b + c; }
const nums = [1, 2, 3];
console.log(sum(...nums)); // 6
数组解构也是ES6的重要特性:
javascript复制const [first, second, ...rest] = [1, 2, 3, 4];
console.log(first); // 1
console.log(rest); // [3, 4]
4.2 类型化数组
JavaScript还提供了类型化数组(Typed Arrays),用于处理二进制数据:
javascript复制// 创建一个8字节的缓冲区
const buffer = new ArrayBuffer(8);
// 创建一个指向缓冲区的32位整数视图
const int32View = new Int32Array(buffer);
int32View[0] = 42;
console.log(int32View[0]); // 42
4.3 ES2019-ES2023新增方法
较新的ECMAScript标准为数组添加了更多实用方法:
flat()/flatMap():扁平化数组at():支持负索引的访问方法findLast()/findLastIndex():从末尾查找
javascript复制// flat示例
const nested = [1, [2, [3]]];
console.log(nested.flat(2)); // [1, 2, 3]
// at示例
const arr = [1, 2, 3];
console.log(arr.at(-1)); // 3 (等同于arr[arr.length - 1])
5. 数组的常见应用场景与性能优化
5.1 数据转换与处理
数组方法链式调用可以优雅地处理数据转换:
javascript复制const products = [
{name: 'iPhone', price: 999, stock: 10},
{name: 'MacBook', price: 1299, stock: 5},
{name: 'iPad', price: 799, stock: 0}
];
// 获取有库存的产品名称,并按价格排序
const availableProducts = products
.filter(p => p.stock > 0)
.sort((a, b) => a.price - b.price)
.map(p => p.name);
console.log(availableProducts); // ['iPhone', 'MacBook']
5.2 数组去重的多种方法
根据不同的场景需求,有多种数组去重的方法:
javascript复制// 使用Set(ES6)
const unique1 = [...new Set([1, 2, 2, 3])]; // [1, 2, 3]
// 使用filter
const unique2 = [1, 2, 2, 3].filter((item, index, arr) => {
return arr.indexOf(item) === index;
});
// 使用reduce
const unique3 = [1, 2, 2, 3].reduce((acc, cur) => {
return acc.includes(cur) ? acc : [...acc, cur];
}, []);
5.3 大数组性能优化
处理大型数组时,性能优化尤为重要:
-
避免不必要的中间数组:链式调用会创建中间数组,对于大数组应考虑使用循环
-
使用惰性计算:考虑使用生成器函数处理大数据集
javascript复制function* bigArrayGenerator() {
for (let i = 0; i < 1000000; i++) {
yield i;
}
}
for (const num of bigArrayGenerator()) {
// 处理每个元素,内存友好
}
- 使用Web Workers:将大数组处理放到后台线程
javascript复制// 主线程
const worker = new Worker('array-worker.js');
worker.postMessage({data: largeArray});
// array-worker.js
self.onmessage = function(e) {
const result = processLargeArray(e.data);
self.postMessage(result);
};
6. 数组与其他数据结构的交互
6.1 数组与字符串的转换
join()和split()是数组与字符串相互转换的主要方法:
javascript复制const str = 'apple,banana,orange';
const arr = str.split(','); // ['apple', 'banana', 'orange']
const newStr = arr.join(';'); // 'apple;banana;orange'
6.2 数组与对象的转换
将数组转为对象:
javascript复制const arr = [['key1', 'value1'], ['key2', 'value2']];
const obj = Object.fromEntries(arr);
// {key1: 'value1', key2: 'value2'}
将对象转为数组:
javascript复制const obj = {a: 1, b: 2};
const entries = Object.entries(obj); // [['a', 1], ['b', 2]]
const keys = Object.keys(obj); // ['a', 'b']
const values = Object.values(obj); // [1, 2]
6.3 数组与Set/Map的交互
javascript复制// 数组转Set
const set = new Set([1, 2, 2, 3]); // Set {1, 2, 3}
// Set转数组
const arrFromSet = [...set]; // [1, 2, 3]
// 数组转Map
const map = new Map([['key1', 'value1'], ['key2', 'value2']]);
// Map转数组
const arrFromMap = [...map]; // [['key1', 'value1'], ['key2', 'value2']]
7. 数组的特殊用例与边界情况
7.1 稀疏数组
JavaScript允许创建稀疏数组(含有"空洞"的数组):
javascript复制const sparse = [1, , 3]; // 索引1处是empty
console.log(sparse.length); // 3
console.log(1 in sparse); // false
稀疏数组在迭代时需要注意:
javascript复制const sparse = [1, , 3];
sparse.forEach(item => console.log(item)); // 输出1和3,跳过empty
7.2 数组的"类数组"对象
许多JavaScript对象看起来像数组但不是真正的数组:
javascript复制function example() {
console.log(Array.isArray(arguments)); // false
console.log(arguments.length); // 实际参数数量
}
example(1, 2, 3);
将类数组转为真正数组的方法:
javascript复制function example() {
// ES5方式
const arr1 = Array.prototype.slice.call(arguments);
// ES6方式
const arr2 = Array.from(arguments);
}
7.3 数组的最大长度限制
JavaScript数组有理论上的最大长度限制:
javascript复制console.log(Math.pow(2, 32) - 1); // 4294967295
const maxArray = new Array(Math.pow(2, 32) - 1);
// 实际中浏览器/环境可能有更低限制
在实际项目中,我遇到过处理大型数组时浏览器崩溃的情况。解决方案通常是分块处理或使用Web Workers。例如,当需要处理超过10万条数据的数组时:
javascript复制function processLargeArrayInChunks(array, chunkSize, processChunk) {
let index = 0;
function doChunk() {
const chunk = array.slice(index, index + chunkSize);
processChunk(chunk);
index += chunkSize;
if (index < array.length) {
setTimeout(doChunk, 0); // 让出主线程
}
}
doChunk();
}
