集合引用类型
Array 类型
1. 数组的创建与初始化
javascript
// 字面量创建
let fruits = ['apple', 'banana', 'orange'];
// 构造函数创建
let numbers = new Array(3); // 创建长度为3的空数组
let colors = new Array('red', 'green', 'blue');
// Array.from() 创建
let arrayFromSet = Array.from(new Set([1, 2, 3]));
let arrayFromString = Array.from('hello'); // ['h', 'e', 'l', 'l', 'o']
// Array.of() 创建
let nums = Array.of(1); // [1]
let mixed = Array.of(1, 'a', true); // [1, 'a', true]
// 类数组转换
function convertArgs() {
let args = Array.from(arguments);
return args;
}
2. 数组检测
javascript
// 检测数组
let arr = [1, 2, 3];
console.log(Array.isArray(arr)); // true
console.log(arr instanceof Array); // true
console.log(Object.prototype.toString.call(arr) === '[object Array]'); // true
// 类数组对象
let arrayLike = {
0: 'a',
1: 'b',
length: 2
};
console.log(Array.isArray(arrayLike)); // false
3. 数组方法详解
3.1 添加和删除元素
javascript
let arr = ['a', 'b', 'c'];
// 尾部操作
arr.push('d'); // 返回新长度 4
let last = arr.pop(); // 返回 'd'
// 头部操作
arr.unshift('x'); // 返回新长度 4
let first = arr.shift(); // 返回 'x'
// 任意位置操作
arr.splice(1, 1); // 删除位置1的元素
arr.splice(1, 0, 'y'); // 在位置1插入'y'
arr.splice(1, 1, 'z'); // 替换位置1的元素
// 实际应用:维护一个固定大小的队列
class FixedQueue {
constructor(size) {
this.size = size;
this.queue = [];
}
enqueue(item) {
this.queue.push(item);
if (this.queue.length > this.size) {
this.queue.shift();
}
return this.queue;
}
}
let messageQueue = new FixedQueue(3);
console.log(messageQueue.enqueue('A')); // ['A']
console.log(messageQueue.enqueue('B')); // ['A', 'B']
console.log(messageQueue.enqueue('C')); // ['A', 'B', 'C']
console.log(messageQueue.enqueue('D')); // ['B', 'C', 'D']
3.2 数组搜索和位置方法
javascript
let numbers = [1, 2, 3, 2, 1];
// 基本搜索
console.log(numbers.indexOf(2)); // 1
console.log(numbers.lastIndexOf(2)); // 3
console.log(numbers.includes(4)); // false
// 使用断言函数搜索
let people = [
{ name: 'John', age: 30 },
{ name: 'Jane', age: 25 },
{ name: 'Jim', age: 35 }
];
let youngPerson = people.find(person => person.age < 30);
console.log(youngPerson); // { name: 'Jane', age: 25 }
let youngPersonIndex = people.findIndex(person => person.age < 30);
console.log(youngPersonIndex); // 1
// 实际应用:查找最近的满足条件的元素
function findNearest(arr, target, compare) {
return arr.reduce((nearest, current) => {
return Math.abs(compare(current) - target) < Math.abs(compare(nearest) - target)
? current
: nearest;
});
}
let temperatures = [
{ city: 'New York', temp: 20 },
{ city: 'London', temp: 18 },
{ city: 'Paris', temp: 22 },
{ city: 'Tokyo', temp: 25 }
];
let targetTemp = 21;
let nearestCity = findNearest(temperatures, targetTemp, item => item.temp);
console.log(nearestCity); // { city: 'New York', temp: 20 }
3.3 迭代方法
javascript
let numbers = [1, 2, 3, 4, 5];
// map: 转换数组元素
let doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// filter: 过滤数组元素
let evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // [2, 4]
// reduce: 累积操作
let sum = numbers.reduce((acc, cur) => acc + cur, 0);
console.log(sum); // 15
// 链式调用示例
let result = numbers
.filter(num => num % 2 === 0) // 过滤偶数
.map(num => num * 2) // 每个数乘2
.reduce((acc, cur) => acc + cur, 0); // 求和
console.log(result); // 12
// 实际应用:数据转换和统计
let orders = [
{ id: 1, total: 200, status: 'completed' },
{ id: 2, total: 300, status: 'pending' },
{ id: 3, total: 150, status: 'completed' },
{ id: 4, total: 450, status: 'pending' }
];
// 按状态分组并计算总金额
let orderStats = orders.reduce((stats, order) => {
if (!stats[order.status]) {
stats[order.status] = 0;
}
stats[order.status] += order.total;
return stats;
}, {});
console.log(orderStats);
// { completed: 350, pending: 750 }
Map 类型
1. Map 的基本用法
javascript
// 创建和初始化
let map = new Map([
['name', 'John'],
['age', 30]
]);
// 添加和获取元素
map.set('city', 'New York');
console.log(map.get('name')); // 'John'
// 检查和删除元素
console.log(map.has('age')); // true
map.delete('age');
console.log(map.size); // 2
// 遍历
map.forEach((value, key) => {
console.log(`${key}: ${value}`);
});
// 实际应用:缓存系统
class Cache {
constructor(maxAge = 5000) {
this.cache = new Map();
this.maxAge = maxAge;
}
set(key, value) {
this.cache.set(key, {
value,
timestamp: Date.now()
});
}
get(key) {
const item = this.cache.get(key);
if (!item) return null;
if (Date.now() - item.timestamp > this.maxAge) {
this.cache.delete(key);
return null;
}
return item.value;
}
clear() {
this.cache.clear();
}
}
let cache = new Cache(2000); // 2秒过期
cache.set('user', { name: 'John' });
setTimeout(() => {
console.log(cache.get('user')); // null(已过期)
}, 3000);
Set 类型
1. Set 的基本用法
javascript
// 创建和初始化
let set = new Set([1, 2, 3, 3, 4]); // 重复值会被忽略
// 添加和删除元素
set.add(5);
set.delete(1);
console.log(set.size); // 4
// 检查元素
console.log(set.has(2)); // true
// 转换为数组
let array = Array.from(set);
// 或者
let array2 = [...set];
// 实际应用:数组去重
function uniqueArray(arr) {
return [...new Set(arr)];
}
// 实际应用:标签系统
class TagManager {
constructor() {
this.tags = new Set();
}
addTag(tag) {
this.tags.add(tag.toLowerCase());
}
removeTag(tag) {
this.tags.delete(tag.toLowerCase());
}
hasTag(tag) {
return this.tags.has(tag.toLowerCase());
}
getAllTags() {
return [...this.tags];
}
}
let tagManager = new TagManager();
tagManager.addTag('JavaScript');
tagManager.addTag('TypeScript');
tagManager.addTag('javascript'); // 不会重复添加
console.log(tagManager.getAllTags()); // ['javascript', 'typescript']
WeakMap 和 WeakSet
1. WeakMap 使用场景
javascript
// 私有数据存储
const privateData = new WeakMap();
class Person {
constructor(name) {
privateData.set(this, { name });
}
getName() {
return privateData.get(this).name;
}
}
let person = new Person('John');
console.log(person.getName()); // 'John'
// 当 person 被垃圾回收时,privateData 中的数据也会被回收
// DOM节点数据关联
const nodeData = new WeakMap();
function setNodeData(node, data) {
nodeData.set(node, data);
}
function getNodeData(node) {
return nodeData.get(node);
}
// 使用示例
let div = document.createElement('div');
setNodeData(div, { clicks: 0 });
// 当 div 被删除时,相关数据会被自动清理
2. WeakSet 使用场景
javascript
// 追踪对象实例
const instances = new WeakSet();
class Widget {
constructor() {
instances.add(this);
}
isValid() {
return instances.has(this);
}
}
let widget = new Widget();
console.log(widget.isValid()); // true
// 防止重复调用
const called = new WeakSet();
function oneTimeFunction(obj) {
if (called.has(obj)) {
throw new Error('Function can only be called once per object!');
}
called.add(obj);
// 执行操作...
}
最佳实践
选择合适的集合类型
- 需要键值对?使用 Map
- 需要唯一值集合?使用 Set
- 需要弱引用?使用 WeakMap/WeakSet
- 需要数值索引?使用 Array
数组操作优化
- 使用 Array.from() 代替 slice 复制数组
- 使用解构和展开运算符进行数组操作
- 适当使用 TypedArray 处理二进制数据
Map vs Object
- Map 的键可以是任何类型
- Map 保持插入顺序
- Map 在频繁增删键值对时性能更好
Set vs Array
- 使用 Set 存储唯一值
- Set 提供了高效的查找和删除操作
- Set 适合用于数学集合运算
性能优化
- 预先设定数组大小可以提高性能
- 使用适当的数组方法避免不必要的遍历
- 考虑使用 TypedArray 处理大量数值数据
练习题
- 实现一个函数,找出数组中出现次数最多的元素。
- 使用 Map 实现一个简单的事件发布订阅系统。
- 实现一个函数,对嵌套数组进行扁平化处理。
- 使用 Set 实现两个数组的交集、并集和差集操作。