目录
JavaScript数据结构
一、基础数据结构
1. 数组(Array)
2. 对象(Object)
二、ES6+ 高级数据结构
1. Map
2. Set
3. WeakMap 与 WeakSet
三、类型化数组(Typed Arrays)
四、其他数据结构实现
1. 栈(Stack)
2. 队列(Queue)
3. 链表(Linked List)
五、数据结构选择指南
六、最佳实践
JavaScript中的for循环
1. 传统 for 循环
语法
示例
特点
2. for...of 循环
语法
示例
特点
3. for...in 循环
语法
示例
特点
4. Array.prototype.forEach
语法
示例
特点
5. 性能与选择指南
性能对比
如何选择?
6. 常见陷阱
1. 修改数组长度
2. 闭包问题
3. 遍历稀疏数组
JavaScript数据结构
一、基础数据结构
1. 数组(Array)
-
定义:有序元素集合,可存储任意类型值。
-
创建方式:
let arr1 = [1, "a", true]; // 字面量 let arr2 = new Array(3); // 创建长度3的空数组(慎用)
-
核心方法:
-
增删元素:
arr.push(4); // 末尾添加 → [1, 2, 3, 4] arr.pop(); // 移除末尾 → [1, 2] arr.unshift(0); // 开头添加 → [0, 1, 2] arr.shift(); // 移除开头 → [1, 2]
-
操作数组:
arr.splice(1, 1, "x"); // 从索引1删除1个元素,插入"x" → [1, "x", 3] arr.slice(1, 3); // 截取索引1到3(不包含3)→ ["x", 3] arr.concat([4, 5]); // 合并数组 → [1, 2, 3, 4, 5]
-
迭代与转换:
arr.map(x => x * 2); // 映射新数组 → [2, 4, 6] arr.filter(x => x > 1); // 过滤 → [2, 3] arr.reduce((sum, x) => sum + x, 0); // 累加 → 6
-
-
注意事项:
-
稀疏数组:空位(如
new Array(3)
)可能引发意外行为。 -
引用类型:数组赋值传递引用,需用
[...arr]
或arr.slice()
克隆。
-
2. 对象(Object)
-
定义:键值对集合,键为字符串或 Symbol。
-
创建方式:
let obj1 = { name: "Alice", age: 25 }; // 字面量 let obj2 = new Object(); // 构造函数
-
核心操作:
-
增删查改:
obj.email = "alice@example.com"; // 添加属性 delete obj.age; // 删除属性 console.log("name" in obj); // 检查属性是否存在 → true
-
遍历:
Object.keys(obj); // 返回键数组 → ["name", "email"] Object.values(obj); // 返回值数组 → ["Alice", "alice@example.com"] for (let key in obj) { console.log(key, obj[key]); } // 遍历自身及原型链属性
-
-
注意事项:
-
原型污染:避免修改
Object.prototype
。 -
键顺序:ES6 后字符串键按插入顺序保留,但数字键优先排序。
-
二、ES6+ 高级数据结构
1. Map
-
特点:
-
键可以是任意类型(对象、函数等)。
-
保持插入顺序。
-
-
核心方法:
let map = new Map(); map.set("key", "value"); // 添加键值对 map.get("key"); // 获取值 → "value" map.has("key"); // 检查存在 → true map.delete("key"); // 删除键值对 map.size; // 获取条目数
-
适用场景:需要复杂键或频繁增删键值对时,优于 Object。
2. Set
-
特点:存储唯一值,自动去重。
-
核心方法:
let set = new Set(); set.add(1); // 添加值 set.add(2); set.has(1); // 检查存在 → true set.delete(1); // 删除值 set.size; // 获取元素数量
-
适用场景:去重、集合运算(并集、交集等)。
3. WeakMap 与 WeakSet
-
特点:
-
弱引用:键必须是对象,不阻止垃圾回收。
-
不可迭代:无
size
、clear()
或遍历方法。
-
-
使用场景:
-
WeakMap:存储对象关联的私有数据或元数据。
let wm = new WeakMap(); let obj = {}; wm.set(obj, "secret");
-
WeakSet:标记对象(如跟踪已处理对象)。
let ws = new WeakSet(); ws.add(obj);
-
三、类型化数组(Typed Arrays)
用于处理二进制数据(如图像、音频),与 ArrayBuffer
配合使用。
-
常见类型:
-
Int8Array
、Uint8Array
(8位整数) -
Int16Array
、Float32Array
等。
-
-
示例:
let buffer = new ArrayBuffer(16); // 分配16字节内存 let int32View = new Int32Array(buffer); // 32位整数视图(4个元素) int32View[0] = 42;
四、其他数据结构实现
JavaScript 未内置,但可通过基础结构模拟:
1. 栈(Stack)
-
后进先出(LIFO),用数组实现:
class Stack {constructor() { this.items = []; }push(element) { this.items.push(element); }pop() { return this.items.pop(); }peek() { return this.items[this.items.length - 1]; } }
2. 队列(Queue)
-
先进先出(FIFO):
class Queue {constructor() { this.items = []; }enqueue(element) { this.items.push(element); }dequeue() { return this.items.shift(); } // 时间复杂度 O(n) }
3. 链表(Linked List)
-
节点链接实现:
class Node {constructor(value) {this.value = value;this.next = null;} } class LinkedList {constructor() { this.head = null; }append(value) { /* 实现添加逻辑 */ } }
五、数据结构选择指南
场景 | 推荐数据结构 | 理由 |
---|---|---|
有序集合,需快速访问索引 | 数组(Array) | 索引操作时间复杂度 O(1) |
键值对,键为字符串或 Symbol | 对象(Object) | 语法简洁,查找速度快 |
键为任意类型,需维护插入顺序 | Map | 支持复杂键,有序 |
存储唯一值 | Set | 自动去重,集合运算高效 |
高频增删元素(如队列) | 链表(自定义实现) | 避免数组 shift() 的 O(n) 复杂度 |
二进制数据处理 | 类型化数组(Typed Array) | 内存高效,适合底层操作 |
六、最佳实践
-
优先使用 ES6+ 数据结构:如
Map
、Set
替代传统对象和数组,提升代码可读性。 -
注意引用类型副作用:克隆数据避免意外修改。
-
性能敏感场景优化:如用
Int32Array
替代普通数组处理大量数值。 -
垃圾回收考虑:使用
WeakMap
/WeakSet
管理对象关联数据,防止内存泄漏。
JavaScript中的for
循环
1. 传统 for
循环
最基础的循环结构,通过明确的初始化、条件和迭代器控制循环。
语法
for (初始化; 条件; 迭代器) {// 循环体
}
示例
// 遍历数组
const arr = [1, 2, 3];
for (let i = 0; i < arr.length; i++) {console.log(arr[i]); // 输出 1, 2, 3
}// 倒序循环
for (let i = arr.length - 1; i >= 0; i--) {console.log(arr[i]); // 输出 3, 2, 1
}
特点
-
灵活控制:可自定义步长(如
i += 2
)、跳过某次循环(continue
)或提前退出(break
)。 -
性能优化:预存数组长度(如
let len = arr.length
)避免重复计算。 -
作用域:使用
let
声明变量时,每次循环会创建新的块级作用域。
2. for...of
循环
遍历可迭代对象(如数组、字符串、Map、Set、生成器等)的值。
语法
for (const value of iterable) {// 使用 value
}
示例
const arr = ['a', 'b', 'c'];
for (const val of arr) {console.log(val); // 输出 'a', 'b', 'c'
}// 遍历字符串
for (const char of 'Hello') {console.log(char); // 输出 H, e, l, l, o
}
特点
-
简洁性:无需索引,直接获取值。
-
支持异步:可与
await
结合使用(需在async
函数中)。 -
不适用于普通对象:默认对象不可迭代(除非自行实现
Symbol.iterator
)。
3. for...in
循环
遍历对象的可枚举属性(包括原型链上的属性)。
语法
for (const key in object) {// 使用 key 访问属性值:object[key]
}
示例
const obj = { a: 1, b: 2 };
for (const key in obj) {console.log(key, obj[key]); // 输出 a 1, b 2
}// 遍历数组(不推荐!)
const arr = [1, 2, 3];
arr.foo = 'bar';
for (const key in arr) {console.log(key); // 输出 0, 1, 2, 'foo'
}
特点
-
遍历对象属性:适合处理键值对。
-
可能遍历原型链:需用
hasOwnProperty
过滤:for (const key in obj) {if (obj.hasOwnProperty(key)) {// 仅处理自身属性} }
-
不保证顺序:现代引擎通常按属性添加顺序遍历,但复杂场景可能不一致。
4. Array.prototype.forEach
数组专用的高阶函数,遍历每个元素。
语法
array.forEach((value, index, array) => {// 循环体
});
示例
const arr = [1, 2, 3];
arr.forEach((value, index) => {console.log(index, value); // 输出 0 1, 1 2, 2 3
});
特点
-
不可中断:无法使用
break
或continue
,需通过return
跳过当前迭代。 -
链式调用:可与其他数组方法(如
map
、filter
)配合。 -
异步陷阱:回调函数中的
await
不会暂停外层循环。
5. 性能与选择指南
性能对比
-
最快:传统
for
循环(尤其预存长度时)。 -
适中:
for...of
(底层使用迭代器协议)。 -
较慢:
forEach
(函数调用开销)。
如何选择?
场景 | 推荐方式 |
---|---|
需要索引或复杂控制 | 传统 for 循环 |
遍历数组值(无需索引) | for...of |
遍历对象属性 | for...in + 过滤 |
简单数组操作 | forEach |
处理稀疏数组 | 传统 for + 判空 |
6. 常见陷阱
1. 修改数组长度
const arr = [1, 2, 3];
for (let i = 0; i < arr.length; i++) {arr.pop(); // 可能导致无限循环或跳过元素
}
2. 闭包问题
for (var i = 0; i < 3; i++) {setTimeout(() => console.log(i)); // 输出 3, 3, 3
}
// 解决:改用 let 或 IIFE
3. 遍历稀疏数组
const arr = [1, , 3]; // 中间是空位
arr.forEach(v => console.log(v)); // 输出 1, 3(跳过空位)
for (const v of arr) console.log(v); // 输出 1, undefined, 3