目录
Map
Map.set()
Map.get()
Map.delete()
Map.has()
Map.values()
Map.entries()
Map.clear()
选择Object 还是Map
数据转换
转为数组
转为 JSON
对象转为 Map
数组转为 Map
转为Object
WeakMap
基本API
弱键
不可迭代
Set
创建Set实例
Set实例转数组
size属性
add()
has()
. delete()
clear()
迭代
WeakSet
迭代与扩展操作
映射(Map)是 ECMAScript 6 规范中引入的一种数据结构。这是一种存储键值对列表很方便的方法,类似于其他编程语言中的词典或者哈希表。常用的 Map
方法有:
set(key, value)
、get(key)
、delete(key)
、has(key)
、values()
、key/value
迭代器 entries()
、clear()
等。JavaScript 的对象
Object
,本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键,这给使用带来了很大的限制。为了解决这个问题,ECMAScript 6 引入了Map
数据结构。它类似于对象,也是键值对的集合,但是"键
"的范围不仅仅局限于字符串,而是各种类型的值(包括对象)都可以当作键。也就是说,Object
结构(对象结构)提供了"字符串—值"的对应,而Map
结构提供了"值—值"的对应,是一种更完善的Hash
结构的实现。
输出结果为:
Map {
'seo' => {
keywords: 'infoq、Map',
description: 'Map对象是一种简单的键/值映射,其中的键和值可以是任意值(原始值或对象的值)'
},
'title' => 'javascript es6的map映射'
}
object
为数据类型 Map 赋值的方法 map.set(key,value)
,可以用于增加新的键/值对或者修改键/值对,返回整个 Map
对象。
myMap.set(key, value);
key
:要添加至相应 Map 对象的元素的键。value
:要添加至相应 Map 对象的元素的值。返回值 返回 Map 对象。
const page_info = new Map()
// 设置值
page_info.set("seo", {
"keywords": "infoq、Map",
"description": "Map对象是一种简单的键/值映射,其中的键和值可以是任意值(原始值或对象的值)"
});
console.log(page_info);
page_info.set("seo", "seo信息");
console.log(page_info);
使用 get(key)
获取键值,如果获取的 key->value
不存则返回 undefined
。
myMap.get(key);
key
:必须参数,也是它唯一的参数,要从目标 Map 对象中获取的元素的键。返回值 返回一个 Map 对象中与指定键相关联的值,如果找不到这个键则返回 undefined
。
const page_info = new Map();
page_info.set("title", "javascript es6的map映射");
const title = page_info.get("title");
const seo_info = page_info.get("seo");
console.log(title); // javascript es6的map映射
console.log(seo_info); // undefinedC
map.delete(key)
删除指定 key
的键值对,返回成功或失败结果,删除成功返回 true
,删除失败返回 false
。
myMap.delete(key);
key
:必须,从 Map 对象中移除的元素的键。返回值 返回值为一个 Boolean
值,如果 Map 对象中存在该元素,则移除它并返回 true
;否则如果该元素不存在则返回 false
。
const page_info = new Map();
page_info.set("title", "javascript es6的map映射");
page_info.set("author", "infoq");
console.log(page_info); // Map { 'title' => 'javascript es6的map映射', 'author' => 'infoq' }
const deleted_author = page_info.delete("author");
const deleted_seo = page_info.delete("seo");
console.log(deleted_author); // true
console.log(deleted_seo); // false
console.log(page_info);
判断指定key是否存在。
语法
myMap.has(key);
key
:必填. 用来检测是否存在指定元素的键值。返回值 返回值为一个 Boolean
值,如果指定元素存在于 Map 中,则返回 true
,其他情况返回 false
。
const page_info = new Map();
page_info.set("title", "javascript es6的map映射");
console.log(page_info); // Map { 'title' => 'javascript es6的map映射' }
console.log(page_info.has("title")); // true
console.log(page_info.has("seo")); // false
获取所有键的值。
语法
myMap.values()
返回值 一个新的 Map 可迭代对象。
const page_info = new Map();
page_info.set("title", "javascript es6的map映射");
page_info.set("author", "infoq");
console.log(page_info.values()); // [Map Iterator] { 'javascript es6的map映射', 'infoq' }
返回一个新的包含 [key, value]
对的 Iterator
对象,返回的迭代器的迭代顺序与 Map
对象的插入顺序相同。
语法
myMap.entries()
返回值一个新的 Map 迭代器对象。
const page_info = new Map();
page_info.set("title", "javascript es6的map映射");
page_info.set("author", "infoq");
console.log(page_info.entries());
移除Map对象中的所有元素。
语法
myMap.clear();
返回值 清除所有元素,返回 undefined
。
const page_info = new Map();
page_info.set("title", "javascript es6的map映射");
page_info.set("author", "infoq");
page_info.clear();
console.log(page_info); // Map {}
map是键值的映射,对象是作为属性进行保存。
Map 是一个集合,可以与数组、对象进行转换。
Map 转为数组最方便方法是使用扩展运算符 ...
,如下:
const page_info = new Map();
page_info.set("title", "javascript es6的map映射");
page_info.set("author", "infoq");
console.log([...page_info]); // [ [ 'title', 'javascript es6的map映射' ], [ 'author'
Map 转为 JSON ,步骤是先把 Map 转为对象,即前面的 mapToObj
,然后使用 JSON.stringify
方法,如下:
function mapToObj(map) {
const obj = Object.create(null);
map.forEach((v,k)=>{
obj[k] = v;
});
return obj;
}
function mapToJson(map){
return JSON.stringify(mapToObj(map));
}
const page_info = new Map();
page_info.set("title", "javascript es6的map映射");
page_info.set("author", "infoq");
console.log( mapToJson(page_info)); // {"title":"javascript es6的map映射","author":"infoq"}
对象转为 Map 映射通过 Object.entries()
。
const page_info = {
title:"javascript es6的map映射",
author:"infoq"
};
console.log(new Map(Object.entries(page_info))); // Map { 'title' => 'javascript es6的map映射', 'author' => 'infoq' }
将数组传入 Map 构造函数即可,即 new Map(array)
,如下:
const page_info = [
["title","javascript es6的map映射"],
["author","infoq"]
];
console.log(new Map(page_info)); // Map { 'title' => 'javascript es6的map映射', 'author' => 'infoq' }
function mapToObj(map) {
const obj = Object.create(null);
map.forEach((v, k) => {
obj[k] = v;
});
return obj;
}
const page_info = new Map();
page_info.set("title", "javascript es6的map映射");
page_info.set("author", "infoq");
console.log(mapToObj(page_info));
WeakMap 是Map 的“兄弟”类型,其API 也是Map 的子集。WeakMap 中的“weak”(弱),描述的是JavaScript 垃圾回收程序对待“弱映射”中键的方式。
可以使用new 关键字实例化一个空的WeakMap:
const wm = new WeakMap();
弱映射中的键只能是Object 或者继承自Object 的类型,尝试使用非对象设置键会抛出TypeError。值的类型没有限制。
const key1 = {id: 1},
key2 = {id: 2},
key3 = {id: 3};
// 使用嵌套数组初始化弱映射
const wm1 = new WeakMap([
[key1, "val1"],
[key2, "val2"],
[key3, "val3"]
]);
alert(wm1.get(key1)); // val1
alert(wm1.get(key2)); // val2
alert(wm1.get(key3)); // val3
键不是对象时,报错:
// 初始化是全有或全无的操作
// 只要有一个键无效就会抛出错误,导致整个初始化失败
const wm2 = new WeakMap([
[key1, "val1"],
["BADKEY", "val2"],
[key3, "val3"]
]);
// TypeError: Invalid value used as WeakMap key
typeof wm2;
// ReferenceError: wm2 is not defined
初始化之后可以使用set()
再添加键/值对,可以使用get()
和has()
查询,还可以使用delete()
删除。
WeakMap 中“weak”表示弱映射的键是“弱弱地拿着”的。意思就是,这些键不属于正式的引用,不会阻止垃圾回收。但要注意的是,弱映射中值的引用可不是“弱弱地拿着”的。只要键存在,键/值对就会存在于映射中,并被当作对值的引用,因此就不会被当作垃圾回收。
const wm = new WeakMap();
wm.set({}, "val");
set()方法初始化了一个新对象并将它用作一个字符串的键。因为没有指向这个对象的其他引用,所以当这行代码执行完成后,这个对象键就会被当作垃圾回收。然后,这个键/值对就从弱映射中消失了,使其成为一个空映射。在这个例子中,因为值也没有被引用,所以这对键/值被破坏以后,值本身也会成为垃圾回收的目标。
即当这个对象键没有引用时,会被回收,导致这个键值对也会被回收
因为WeakMap 中的键/值对任何时候都可能被销毁,所以并没有给WeakMap提供迭代键值对的能力。
所以无法迭代,所以也不可能在不知道对象引用的情况下从弱映射中取得值。即便代码可以访问WeakMap 实例,也没办法看到其中的内容。
WeakMap实例之所以限制只能用对象作为键,是为了保证只有通过键对象的引用才能取得值。如果允许原始值,那就没办法区分初始化时使用的字符串字面量和初始化之后使用的一个相等的字符串了。
ECMAScript 6 新增的 Set 是一种新集合类型,为这门语言带来集合数据结构。Set 在很多方面都像是加强的 Map,这是因为它们的大多数 API 和行为都是共有的。
使用 new 关键字和 Set 构造函数可以创建一个空集合:
const s = new Set();
如果想在创建的同时初始化实例,则可以给 Set 构造函数传入一个可迭代对象,其中需要包含插入到新集合实例中的元素(Set 可以包含任何 JavaScript 数据类型作为值):
const s = new Set(["val1", 1, true, {}, undefined, function fun() {}]);
注意:Set结构不会添加重复的值
const s = new Set([1, 1, 2, 3, 4, 4, 5, 6, 7, 4, 2, 1]);
Array.from(s); // [1, 2, 3, 4, 5, 6, 7]
经常用Set解决数组去重问题
const arr = [1, 2, 3, 3, 4, 5, 4, 4, 2, 1, 3];
Array.from(new Set(arr)); // [1, 2, 3, 4, 5]
const s = new Set([1, 2, 3]);
Array.from(s); // [1, 2, 3]
size: 获取Set实例的元素个数:
const s = new Set([1, 2, 3]);
s.size; // 3
add(): 添加元素:
const s = new Set();
s.add(1).add(2).add(3);
Array.from(s); // [1, 2, 3]
has(): 查询Set实例是否存在某元素(返回布尔值):
const s = new Set();
s.add(1).add(2).add(3);
s.has(1); // true
delete(): 删除Set实例中某个元素(返回布尔值):
const s = new Set();
s.add(1).add(2);
s.delete(1);
Array.from(s); // [2]
clear(): 清空Set实例:
const s = new Set();
s.add(1).add(2).add(3);
Array.from(s); // [1, 2, 3]
s.clear();
Array.from(s); // []
const s = new Set();
s.add(1).add(2).add(3);
Array.from(s.keys()); // [1, 2, 3]
Array.from(s.values()); // [1, 2, 3]
Array.from(s.entries()); // [[1, 1], [2, 2], [3, 3]]
for-of:
const s = new Set();
s.add(1).add(2).add(3);
for (const i of s) {
console.log(i);
}
// 1
// 2
// 3
forEach
const s = new Set();
s.add(1).add(2).add(3);
s.forEach((value, key) => console.log(key + ' : ' + value));
// 1 : 1
// 2 : 2
// 3 : 3
WeakSet 是Set 的“兄弟”类型,其API 也是Set 的子集。WeakSet 中的“weak”(弱),描述的是JavaScript 垃圾回收程序对待“弱集合”中值的方式。
可以使用new 关键字实例化一个空的WeakSet
const ws = new WeakSet();
弱集合中的值只能是Object 或者继承自Object 的类型,尝试使用非对象设置值会抛出TypeError。
如果想在初始化时填充弱集合,则构造函数可以接收一个可迭代对象,其中需要包含有效的值。可迭代对象中的每个值都会按照迭代顺序插入到新实例中:
const val1 = {id: 1},
val2 = {id: 2},
val3 = {id: 3};
// 使用数组初始化弱集合
const ws1 = new WeakSet([val1, val2, val3]);
alert(ws1.has(val1)); // true
alert(ws1.has(val2)); // true
alert(ws1.has(val3)); // true
初始化之后可以使用add()
再添加新值,可以使用has()
查询,还可以使用delete()
删除。弱值性质与不可迭代性质与WeakMap的原理相同。
有4 种原生集合类型定义了默认迭代器:
意味着上述所有类型都支持顺序迭代,都可以传入for-of 循环:
for (const iterableThing of iterableThings) {
for (const x of iterableThing) {
console.log(x);
}
}
意味着所有这些类型都兼容扩展操作符。扩展操作符在对可迭代对象执行浅复制时特别有用,只需简单的语法就可以复制整个对象:
let arr1 = [1, 2, 3];
let arr2 = [...arr1];
console.log(arr1); // [1, 2, 3]
console.log(arr2); // [1, 2, 3]
console.log(arr1 === arr2); // false
对于期待可迭代对象的构造函数,只要传入一个可迭代对象就可以实现复制:
let map1 = new Map([[1, 2], [3, 4]]);
let map2 = new Map(map1);
console.log(map1); // Map {1 => 2, 3 => 4}
console.log(map2); // Map {1 => 2, 3 => 4}
也可以构建数组的部分元素:
let arr1 = [1, 2, 3];
let arr2 = [0, ...arr1, 4, 5];
console.log(arr2); // [0, 1, 2, 3, 4, 5]
浅复制意味着只会复制对象引用,复制指针,指向的还是同一个对象。
let arr1 = [{}];
let arr2 = [...arr1];
arr1[0].foo = 'bar';
console.log(arr2[0]); // { foo: 'bar' }