重学ES6 | Set和Map
ES6 新增了两个数据结构,一个是 set,另外一个是 map。
1、set
在《你不知道的 JavaScript(下卷)》中是这么定义的:set 是一个值的集合,其中的值唯一(重复会被忽略)。 它类似于数组,但是每个成员的值是唯一的。 set 是一个构造函数,可以通过 new 来创建一个 set 实例。
let set = new Set([1, 2, 3, 1, 4]);
console.log(set); // {1, 2, 3, 4}
上述可以看出,new Set 会自动过滤掉重复值,并返回一个集合,我们可以利用这个特性,来写出简单的数组去重。
let set = new Set([1, 2, 3, 1, 4]);
let arr = [...set];
console.log(arr); // [1, 2, 3, 4]
set 通过 add()来增加成员,将新值放在集合尾部,如果新值跟原集合中的成员重复的话,会被自动过滤掉。
let set = new Set([1, 2, 3, 1, 4]);
set.add(1);
console.log(set); // {1, 2, 3, 4}
let set = new Set([1, 2, 3, 1, 4]);
set.add(0);
console.log(set); // {1, 2, 3, 4, 0}
Set 的构造函数可以接受一个具有 Iterable 接口的其他数据结构作为参数。
// 例1
let set = new Set("string");
console.log(set); // {"s", "t", "r", "i", "n", "g"}
// 例2
function foo(a, b) {
let set = new Set(arguments);
console.log(set); // {1, 2}
}
foo(1, 2);
2、set 的 API
set 的实例属性有两个:
- Set.prototype.constructor:构造函数,默认就是 Set 函数。
- Set.prototype.size: 返回 Set 对象中的值的个数。
实例方法分为两类,一个是操作类,一个是遍历类。 操作类:
- Set.prototype.add(value):在 Set 对象尾部添加一个元素。返回该 Set 对象。
- Set.prototype.clear():移除 Set 对象内的所有元素。
- Set.prototype.delete(value):移除 Set 的中与这个值相等的元素,返回 Set.prototype.has(value)在这个操作前会返回的值(即如果该元素存在,返回 true,否则返回 false)。Set.prototype.has(value)在此后会返回 false。
- Set.prototype.has(value):返回一个布尔值,表示该值在 Set 中存在与否。
// 例1
let set = new Set([1, 2, 3]);
set.clear();
console.log(set); // {}
// 例2
let set = new Set([1, 2, 3]);
let result = set.delete(1);
console.log(result); // true
console.log(set); // {2, 3}
// 例3
let set = new Set([1, 2, 3]);
let result = set.has(1);
console.log(result); // true
遍历类:
- Set.prototype.values():返回一个新的迭代器对象,该对象包含 Set 对象中的按插入顺序排列的所有元素的值。
- Set.prototype.keys():与 values()方法相同,返回一个新的迭代器对象,该对象包含 Set 对象中的按插入顺序排列的所有元素的值。
- Set.prototype.forEach(callbackFn[, thisArg]):按照插入顺序,为 Set 对象中的每一个值调用一次 callBackFn。如果提供了 thisArg 参数,回调中的 this 会是这个参数。
- Set.prototype.entries():返回一个新的迭代器对象,该对象包含 Set 对象中的按插入顺序排列的所有元素的值的[value, value]数组。为了使这个方法和 Map 对象保持相似,每个值的键和值相等。
// 例1
let set = new Set([1, 2, 3]);
let keys = set.keys();
console.log(keys); // {1, 2, 3}
// 例2
let set = new Set([1, 2, 3]);
let values = set.values();
console.log(values); // {1, 2, 3}
上述两个例子,因为 Sset 没有键名,只有值,所以 keys 和 values 的结果是一样的。 Set 同样拥有 forEach 方法:
let set = new Set([1, 2, 3]);
set.forEach((key, value) => {
console.log(`${key}:${value}`);
});
// 1:1
// 2:2
// 3:3
跟数组的 forEach 方法不同的是,它的回调函数的参数是其 key 值和 value 值,但是 Set 没有 key 值,所以其 key 和 value 相等。forEach 的第二个参数是 this 值,其绑定的是回调函数中的 this 值。 最后一个实例方法是 entries:
let set = new Set([1, 2, 3]);
let entries = set.entries();
console.log(entries);
// {
// [1, 1],
// [2, 2],
// [3, 3]
// }
enteries()返回一个遍历器,返回一个键值对,但其键值对均相等。
3、WeakSet
WeakSet 也是一个构造函数,与 Set 一直,WeakSet 和 Set 两者相似但不同,不同点主要有两个:
- WeakSet 的成员值必须是对象(可迭代的对象),而并不像 set 一样可以是原生类型值。
- WeakSet 持弱引用:集合中对象的引用为弱引用。如果没有其他的对 WeakSet 中对象的引用,那么这些对象会被当成垃圾回收掉。这也意味着 WeakSet 中没有存储当前对象的列表。正因为这样,WeakSet 是不可枚举的。
let weakSet = new WeakSet([1, 2]); // Uncaught TypeError: Invalid value used in weak set
let weakSet = new WeakSet([
[1, 2],
[3, 4],
]); // {[1, 2], [3, 4]}
WeakSet 有三个实例方法:
- WeakSet.prototype.add(value):返回构造函数即 WeakSet 本身。
- WeakSet.prototype.delete(value):从该 WeakSet 对象中删除 value 这个元素, 之后 WeakSet.prototype.has(value) 方法便会返回 false。
- WeakSet.prototype.has(value):返回一个布尔值, 表示给定的值 value 是否存在于这个 WeakSet 中。
var ws = new WeakSet();
var foo = {};
var bar = {};
ws.add(foo);
ws.add(bar);
ws.has(foo); // true
ws.has(bar); // true
ws.delete(foo); // 从set中删除 foo 对象
ws.has(foo); // false, foo 对象已经被删除了
ws.has(bar); // true, bar 依然存在
4、Map
在 JS 中对象是创建无序键 / 值对数据结构 [ 也称为 映射(map)] 的主要机制。但是,对象作为映射的主要缺点是不能使用非字符串值作为键。所以在 ES6 提出一个新的数据结构,Map。 Map 和对象很类似,都是键值对的形式,但是 Map 的键可以是任意类型(对象或者原始值),NaN 也可以作为键,不再只局限于字符串。 任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构都可以当作 Map 构造函数的参数。
let map = new Map([[{a:1},3]]);
console.log(map);//{ { a:1 },3}
在 MDN 中清晰的列出了 Map 和 Object 间的不同:
Map | Object | |
---|---|---|
意外的键 | Map 默认情况不包含任何键。只包含显式插入的键。 | 一个 Object 有一个原型, 原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。注意: 虽然 ES5 开始可以用 Object.create(null) 来创建一个没有原型的对象,但是这种用法不太常见。 |
键的类型 | 一个 Map 的键可以是任意值,包括函数、对象或任意基本类型。 | 一个 Object 的键必须是一个 String 或是 Symbol。 |
键的顺序 | Map 中的 key 是有序的。因此,当迭代的时候,一个 Map 对象以插入的顺序返回键值。 | 一个 Object 的键是无序的。注意:自 ECMAScript 2015 规范以来,对象确实保留了字符串和 Symbol 键的创建顺序; 因此,在只有字符串键的对象上进行迭代将按插入顺序产生键。 |
Size | Map 的键值对个数可以轻易地通过 size 属性获取 | Object 的键值对个数只能手动计算 |
迭代 | Map 是 iterable 的,所以可以直接被迭代。 | 迭代一个 Object 需要以某种方式获取它的键然后才能迭代。 |
性能 | 在频繁增删键值对的场景下表现更好。 | 在频繁添加和删除键值对的场景下未作出优化。 |
5、Map 的 API
Map 的实例属性有两个:
- Map.prototype.constructor:返回一个函数,它创建了实例的原型。默认是 Map 函数。
- Map.prototype.size: 返回 Map 对象的键/值对的数量。
实例方法分为两类,一个是操作类,一个是遍历类。 操作类:
- Map.prototype.set(key, value):设置 Map 对象中键的值。返回该 Map 对象。
- Map.prototype.get(key):返回键对应的值,如果不存在,则返回 undefined。
- Map.prototype.has(key):返回一个布尔值,表示 Map 实例是否包含键对应的值。
- Map.prototype.delete(key):如果 Map 对象中存在该元素,则移除它并返回 true;否则如果该元素不存在则返回 false。随后调用 Map.prototype.has(key) 将返回 false 。
- Map.prototype.clear():移除 Map 对象的所有键/值对。
let map = new Map();
let key = {name:"Jack"};
let value = "啥";
map.set(key, value); //{ { name:'Jack' }:'啥'}
map.get(key); // '啥'
map.get(a); // undefined
map.has(key); // true
map.delete(key); // true
map.clear(); // {}
遍历类:
- Map.prototype.entries():返回一个新的 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的 [key, value] 数组。
- Map.prototype.forEach(callbackFn[, thisArg]):按插入顺序,为 Map 对象里的每一键值对调用一次 callbackFn 函数。如果为 forEach 提供了 thisArg,它将在每次回调中作为 this 值。
- Map.prototype.keys():返回一个新的 Iterator 对象, 它按插入顺序包含了 Map 对象中每个元素的键 。
- Map.prototype.values():返回一个新的 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的值 。
let map = new Map([
["name","Jack"],
[{age:25},"啥"],
]);
map.entries(); // MapIterator {"name"=>"Jack",{age:25}=>"啥"}
for (let item of map.entries()) {
console.log(item); // ["name","Jack"] [{age:25},"啥"]
}
map.forEach((value, key, map) => {
console.log(value, key, map); // Jack name Map(2) {"name"=>"Jack",{age: 25}=>"啥"}
//"啥"{age:25} Map(2) {"name" =>"Jack",{age:25}=>"啥"}
});
map.keys(); // MapIterator {"name",{age:25}}
for (let item of map.keys()) {
console.log(item); // "name" {age:25}
}
map.values(); // MapIterator {"Jack","啥"}
for (let item of map.values()) {
console.log(item); // "Jack" "啥"
}
6、Map 和其他数据结构的转换
6.1、Map 转数组
ES6 给出一个最简单的方式,扩展运算符。
let map = new Map([ ["name", "Jack"],
[{ age: 25 }, "啥"],
]);
console.log([...map]); // [['name', 'Jack'], [{age: 25}, '啥']]
除此之外还可以用 Array.from()。
let map = new Map([ ["name", "Jack"],
[{ age: 25 }, "啥"],
]);
console.log(Array.from(map)); // [['name', 'Jack'], [{age: 25}, '啥']]
6.2、Map 转对象
如果所有 Map 的键都是字符串,它可以无损地转为对象。
function strMapToObj(strMap) {
let obj = Object.create(null);
for (let [k, v] of strMap) {
obj[k] = v;
}
return obj;
}
const myMap = new Map().set("yes", true).set("no", false);
strMapToObj(myMap);
// { yes: true, no: false }
6.3 Map 转 JSON
Map 转 JSON 有两个形式,第一种是键名都是字符串的类型的,第二种是键名中包含非字符串类型。 第一种的话直接将 map 转为对象,然后再用 JSON.Stringfy()进行转换。 第二种的话可以转成数组 JSON。
function mapToArrayJson(map) {
return JSON.stringify([...map]);
}
let myMap = new Map().set(true, 7).set({ foo: 3 }, ["abc"]);
mapToArrayJson(myMap);
// '[[true,7],[{"foo":3},["abc"]]]'
7、WeakMap
WeakMap 和 WeakSet 有些类似,都是弱引用,并且成员值的键必须是对象。
let weakMap = new WeakMap([[1,3]]); // Uncaught TypeError: Invalid value used as weak map key
let weakMap = new WeakMap([[{name:"Jack"},3]]); // {{name:'Jack'}:3}
WeakMap 有四个实例方法:
- WeakMap.prototype.delete(key):移除 key 的关联对象。执行后 WeakMap.prototype.has(key)返回 false。
- WeakMap.prototype.get(key):返回 key 关联对象, 或者 undefined(没有 key 关联对象时)。
- WeakMap.prototype.has(key):根据是否有 key 关联对象返回一个 Boolean 值。
- WeakMap.prototype.set(key, value):在 WeakMap 中设置一组 key 关联对象,返回这个 WeakMap 对象。
let weakMap = new WeakMap();
let obj = {name: 'Jack'};
weakMap.set(obj, 3); // {{name:'Jack'}: 3}
weakMap.get(obj); // 3
weakMap.has(obj); // true
weakMap.delete(obj); // true
8、Map的应用
Map 的应用最直接的就是策略模式。举个例子如果一个公司的年终奖为一等奖是电脑,二等奖是手机,三等奖是步步高点读机,四等奖是矿泉水。那一般会这么写:
function getAnnualBonus(level) {
if (level === "一等奖") {
return "电脑";
} else if (level === "二等奖") {
return "手机";
} else if (level === "三等奖") {
return "步步高点读机";
} else if (level === "四等奖") {
return "矿泉水";
}
}
getAnnualBonus("一等奖");
// 或者
function getAnnualBonus(level) {
switch (level) {
case "一等奖":
return "电脑";
case "二等奖":
return "手机";
case "三等奖":
return "步步高点读机";
case "四等奖":
return "矿泉水";
default:
break;
}
}
getAnnualBonus("一等奖");
上述两种写法,如果条件越来越多的话,那写的 if...else...和 case 越来越长,代码相当的臃肿。 运用 Map 来写策略模式,简洁明了:
let annualBonus = new Map([
['一等奖', '电脑'],
['二等奖', '手机'],
['三等奖', '步步高点读机'],
['四等奖', '矿泉水']
]);
function getAnnualBonus(level) {
return annualBonus.get(level);
}
getAnnualBonus("一等奖");
参考:
后语
相关文章:
- 带你重学ES6 | var、let和const的区别
- 带你重学ES6 | Promsie
- 带你重学ES6 | Generator
- 带你重学ES6 | Async和Await
- 带你重学ES6 | Set和Map
- 带你重学ES6 | Symbol(不仅仅只是一个新的数据类型)
- 带你重学ES6 | Exprort(谨记输出的都是变量)
- 带你重学ES6 | proxy和defineProperty)
觉得还可以的,麻烦走的时候能给点个赞,让更多人能看到这篇文章,大家一起学习和探讨!不点赞的都是耍流氓😂
还可以关注我的博客希望能给我的github上点个Start,小伙伴们一定会发现一个问题,我的所有用户名几乎都与番茄有关,因为我真的很喜欢吃番茄❤️!!!
想跟车不迷路的小伙还希望可以关注公众号 前端老番茄 或者扫一扫下面的二维码👇👇👇。
我是一个编程界的小学生,您的鼓励是我不断前进的动力,😄希望能一起加油前进。
本文使用 mdnice 排版
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhfjjgkc
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
excel图片置于文字下方的方法
PHP中文网 06-27 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
微信提示登录环境异常是什么意思原因
PHP中文网 04-09 -
微信运动停用后别人还能看到步数吗
PHP中文网 07-22 -
微信人名旁边有个图标有什么用
PHP中文网 03-11