在JavaScript中,通常使用数组(Array)实现类似列表的存储功能,数组可通过字面量[]或构造函数new Array()创建,支持动态调整长度,常用存储操作包括:push()在末尾添加元素,unshift()在开头添加,pop()删除末尾元素,shift()删除开头元素,并通过索引arr[index]访问或修改元素,数组可存储任意类型数据(如数字、字符串、对象等),并内置map()、filter()等高阶方法处理数据,其有序性和动态特性使其适合存储和管理集合数据,是JavaScript中最基础且常用的数据结构之一。
JavaScript中的List存储:从数组到高级数据结构
在JavaScript开发中,"List"(列表)是最基础也最常用的数据结构之一,它用于存储有序的元素集合,支持动态增删改查操作,是处理数据流、缓存、队列等场景的核心工具,尽管JavaScript没有原生的"List"类型,但我们可以通过数组(Array)或自定义数据结构来实现List的功能,本文将从基础到进阶,全面解析JavaScript中List的存储与使用方法。
JavaScript中的List:数组的原生实现
在JavaScript中,数组(Array)是List最直接、最常用的实现方式,数组本身就是一个有序的列表,支持存储任意类型的元素(数字、字符串、对象,甚至其他数组),并提供了一系列内置方法来操作元素。
创建与初始化List
创建数组(List)有多种方式,最常见的是通过数组字面量或Array构造函数:
// 方式1:数组字面量(推荐)
const list1 = [1, 2, 3, "hello", { name: "Alice" }];
// 方式2:Array构造函数
const list2 = new Array(5); // 创建长度为5的空数组(元素为undefined)
const list3 = new Array(1, 2, 3); // 创建包含1, 2, 3的数组
// 方式3:Array.from(从类数组或可迭代对象创建)
const list4 = Array.from("hello"); // ['h', 'e', 'l', 'l', 'o']
const list5 = Array.from({ length: 3 }, (_, i) => i + 1); // [1, 2, 3]
// 方式4:扩展运算符
const list6 = [...list1]; // 创建list1的浅拷贝
List的基本操作:增删改查
数组的核心操作包括增、删、改、查,这些方法直接对应List的动态管理需求:
(1)查:访问元素
通过索引(从0开始)访问元素,JavaScript数组不支持直接使用负索引访问:
const list = [10, 20, 30, 40];
console.log(list[0]); // 10(第一个元素)
console.log(list[-1]); // undefined(JavaScript数组不支持负索引)
console.log(list[list.length - 1]); // 40(最后一个元素)
// 如果需要支持负索引,可以创建辅助函数
function getNegativeIndex(arr, index) {
return index < 0 ? arr[arr.length + index] : arr[index];
}
console.log(getNegativeIndex(list, -1)); // 40
(2)增:添加元素
-
尾部添加:
push()(返回新长度)const list = [1, 2]; list.push(3); // list变为[1, 2, 3]
-
头部添加:
unshift()(返回新长度)list.unshift(0); // list变为[0, 1, 2, 3]
-
中间添加:
splice(start, deleteCount, ...items)(修改原数组,返回被删除元素)list.splice(2, 0, 1.5); // 在索引2处插入1.5,list变为[0, 1, 1.5, 2, 3]
-
批量添加:使用扩展运算符或concat
list = [...list, 4, 5]; // 尾部添加多个元素 list = [6, 7, ...list]; // 头部添加多个元素
(3)删:删除元素
-
尾部删除:
pop()(返回被删除元素)const removed = list.pop(); // removed=3,list变为[0, 1, 1.5, 2]
-
头部删除:
shift()(返回被删除元素)const removed = list.shift(); // removed=0,list变为[1, 1.5, 2]
-
中间删除:
splice(start, deleteCount)const removed = list.splice(1, 1); // 删除索引1处的元素(1.5),removed=[1.5],list变为[1, 2]
-
条件删除:使用filter
const list = [1, 2, 3, 4, 5]; const filtered = list.filter(item => item % 2 === 0); // [2, 4]
(4)改:修改元素
直接通过索引赋值,或使用splice()替换:
list[0] = 100; // 直接修改,list变为[100, 2]
list.splice(1, 1, 200); // 替换索引1处的元素,list变为[100, 200]
// 批量修改
list.forEach((item, index) => {
if (index % 2 === 0) list[index] = item * 2;
});
List的遍历方法
数组提供了多种遍历方式,适用于不同场景:
-
for循环:最灵活,支持索引和自定义终止条件
for (let i = 0; i < list.length; i++) { console.log(list[i]); } -
forEach:遍历每个元素,无返回值
list.forEach((item, index) => console.log(`${index}: ${item}`)); -
map:返回新数组,对每个元素处理后存入新数组
const doubled = list.map(item => item * 2); // [200, 400]
-
filter:返回符合条件的新数组
const filtered = list.filter(item => item > 100); // [200]
-
reduce:累计计算,返回最终值(如求和、拼接)
const sum = list.reduce((acc, item) => acc + item, 0); // 600
-
for...of:直接遍历元素值
for (const item of list) { console.log(item); } -
entries()、keys()、values():获取迭代器
for (const [index, value] of list.entries()) { console.log(`${index}: ${value}`); }
性能考量:数组的"短板"与替代方案
虽然数组是List的天然选择,但其底层基于连续内存存储,导致在某些场景下性能不佳:
- 中间插入/删除:
splice()、shift()、unshift()需要移动后续元素,时间复杂度为O(n)。 - 头部操作:
shift()和unshift()每次操作都需要重新索引整个数组,性能随数组长度增长而显著下降。
当需要频繁进行中间或头部操作时,可以考虑用链表(Linked List)替代。
自定义链表实现
class ListNode {
constructor(value, next = null) {
this.value = value;
this.next = next;
}
}
class LinkedList {
constructor() {
this.head = null;
this.size = 0;
}
// 在头部添加元素
addFirst(value) {
this.head =