TypeScript Map对象

TypeScript Map对象详解

TypeScript Map对象详解

编程小白的TypeScript Map完全指南 – 用简单语言解释高级概念

Map是ES6引入的一种新的数据结构,用于存储键值对。在TypeScript中,你可以使用Map来存储任何类型的键和值,并保持插入顺序。

Map是什么?

可以把Map想象成一个更强大的键值对集合。它类似于JavaScript中的普通对象,但有几点关键区别:

  • Map的键可以是任何类型(对象、函数、基本类型等)
  • Map会记住键的插入顺序
  • Map有方便的内置方法操作数据
  • Map有size属性可直接获取元素数量
“name” “Alice”
“age” 30
“isStudent” false

在TypeScript中,你可以这样定义一个Map:

// 创建一个空Map
const myMap = new Map<string, number>();

// 创建带有初始值的Map
const userMap = new Map<string, any>([
    ["name", "张三"],
    ["age", 28],
    ["isAdmin", true]
]);

为什么需要Map?

你可能会问:JavaScript已经有普通对象了,为什么还需要Map?

下面展示了Map和普通对象的区别:

普通对象的问题
  • 键只能是字符串或Symbol
  • 不保留插入顺序
  • 没有直接的size属性
  • 原型上有默认属性,可能产生冲突
  • 操作不够方便(如遍历)
Map的优势
  • 键可以是任意类型(对象、函数等)
  • 保持插入顺序
  • 有size属性直接获取大小
  • 干净的集合,没有原型属性
  • 有丰富易用的方法

简单说:当你需要键不是字符串,或者需要保持插入顺序,或者需要更便捷的操作方法时,Map是更好的选择。

适用场景举例

  • 需要对象作为键的缓存系统
  • 需要保持元素顺序的集合
  • 需要频繁增删键值对的场景
  • 需要统计元素数量的情况

Map的基本操作

1. 创建Map

// 空Map
const map1 = new Map<string, number>();

// 带初始值
const map2 = new Map<string, string>([
    ['key1', 'value1'],
    ['key2', 'value2']
]);

// 任意类型键值
const map3 = new Map<any, any>();
map3.set(document.body, 'Body Element'); // DOM元素作为键
map3.set(() => {}, 'Function as key'); // 函数作为键

2. 添加/更新元素

const userMap = new Map<string, any>();

// 添加元素
userMap.set('name', '李四');

// 添加多个
userMap.set('age', 25)
       .set('isAdmin', false); // 链式调用

// 更新元素
userMap.set('age', 26); // 覆盖已有值

3. 获取元素

console.log(userMap.get('name')); // 输出: 李四
console.log(userMap.get('nonExistent')); // 输出: undefined

4. 检查键是否存在

console.log(userMap.has('name')); // true
console.log(userMap.has('email')); // false

5. 删除元素

userMap.delete('isAdmin'); // 删除单个
userMap.clear(); // 删除所有元素

6. 获取大小

console.log(userMap.size); // 输出Map中的元素数量

遍历与转换

遍历Map的方法

const colorMap = new Map<string, string>([
    ['red', '#FF0000'],
    ['green', '#00FF00'],
    ['blue', '#0000FF']
]);

// 1. for...of遍历
for (let [key, value] of colorMap) {
    console.log(`${key}: ${value}`);
}

// 2. forEach方法
colorMap.forEach((value, key) => {
    console.log(`Key: ${key}, Value: ${value}`);
});

// 3. 遍历键
for (let key of colorMap.keys()) {
    console.log(key);
}

// 4. 遍历值
for (let value of colorMap.values()) {
    console.log(value);
}

Map与其他数据结构的转换

Map转数组:

// 转键值对数组
const arr = Array.from(colorMap); // [['red', '#FF0000'], ...]

// 转键数组
const keys = Array.from(colorMap.keys());

// 转值数组
const values = Array.from(colorMap.values());

数组转Map:

const arr = [['apple', 5], ['banana', 3]];
const fruitMap = new Map<string, number>(arr);

Map转对象:

const obj = Object.fromEntries(colorMap);
// { red: "#FF0000", green: "#00FF00", blue: "#0000FF" }

对象转Map:

const obj = {width: 100, height: 200};
const map = new Map<string, number>(Object.entries(obj));

高级特性与注意事项

1. 引用类型作为键

const objKey = { id: 1 };
const map = new Map<object, string>();

map.set(objKey, "对象作为键");

console.log(map.get(objKey)); // "对象作为键"

// 注意:必须是同一个对象引用
console.log(map.get({ id: 1 })); // undefined

对象作为键时,Map使用引用来判断键是否相同,而不是对象内容。

2. NaN作为键

const map = new Map<number, string>();
map.set(NaN, "这不是一个数字");

console.log(map.get(NaN)); // "这不是一个数字"

在Map中,NaN被视为相同的键,这点与普通对象不同。

3. Map的相等性

const map1 = new Map([['a', 1]]);
const map2 = new Map([['a', 1]]);

console.log(map1 === map2); // false
console.log(map1.get('a') === map2.get('a')); // true

即使两个Map有相同的内容,它们也是不同的对象。

4. Map与内存

当使用对象作为键时,即使对象在其他地方不再被使用,Map仍然保留对它的引用,可能导致内存泄漏。在不需要时记得删除:

let obj = { data: "重要数据" };
const map = new Map<object, string>();
map.set(obj, "关联值");

// 不再需要时
map.delete(obj);
obj = null; // 现在可以被垃圾回收

实际应用场景

1. 缓存系统

// 使用Map实现简单缓存
const cache = new Map<string, any>();

function getData(key: string) {
    if (cache.has(key)) {
        console.log('从缓存读取');
        return cache.get(key);
    }
    
    console.log('从服务器获取');
    const data = fetchDataFromServer(key); // 模拟获取数据
    cache.set(key, data);
    return data;
}

2. 数据分组

// 使用Map进行数据分组
const people = [
    { name: 'Alice', department: 'HR' },
    { name: 'Bob', department: 'IT' },
    { name: 'Charlie', department: 'IT' }
];

const byDept = new Map<string, any[]>();

people.forEach(person => {
    const dept = person.department;
    if (!byDept.has(dept)) {
        byDept.set(dept, []);
    }
    byDept.get(dept)?.push(person);
});

console.log(byDept.get('IT')); // 输出IT部门的员工

3. 频率计数器

// 统计元素出现次数
function countFrequency(items: string[]) {
    const frequencyMap = new Map<string, number>();
    
    for (const item of items) {
        frequencyMap.set(item, (frequencyMap.get(item) || 0) + 1);
    }
    
    return frequencyMap;
}

const fruits = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'];
console.log(countFrequency(fruits));
// Map(3) {'apple' => 3, 'banana' => 2, 'orange' => 1}

TypeScript Map对象总结 | 记住:当你需要更强大的键值对功能时,Map是你的好帮手!

© 2023 TypeScript学习指南 | 编程小白也能懂的教程

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部