ES6中提供了新的数据结构Set和Map。
Set类似于Array,但内部存储的数据不允许重复。该方法可接受具有Iterable接口的其他数据结构作为参数。例如:
let set=new Set([1,2,3,3]);
console.log(set);
// Set(3) {1, 2, 3}
由此我们生成了一个Set结构的数据。但需要注意,虽然NaN===NaN不成立,但Set存储数据时’认为’NaN等于自己,于是Set结构只会存储一个NaN。
let set=new Set([NaN,NaN]);
console.log(set);
// Set(1) {NaN}
但{}==={}返回false在Set中会正常应用,即
let set=new Set([{},{}]);
console.log(set);
// Set(2) {{…}, {…}}
将Set转为Array方法有2种:
(1)Set结构的数据同样可以使用可接受Iterable的扩展运算符’…’
let set=new Set([1,2,3,3]);
[...set]
// [1,2,3]
显而易见,我们可以用此方法轻松去重。
(2)Array.from()方法可将具有Iterable或ArrayLike的数据类型转为Array结构的数据
Array.from(new Set([1,2,3]))
// [1,2,3]
2.1 属性 Set.prototype.constructor
作用:构造函数,默认就是Set函数
2.2 属性 Set.prototype.size
作用:返回Set大小
let set=new Set([1,2,3,3]);
set.size
// 3
2.3 方法Set.prototype.add(value)
作用:向Set数据结构中添加数据,可链式添加。
let set=new Set([1,2,3,3]);
set.add(4).add('a')
// Set(5) {1,2,3,4,'a'}
2.4 方法Set.prototype.has(value)
作用:返回一个布尔值,表示该值是否为Set的成员
let set=new Set([1,2,3,3]);
set.has(2)
// true
2.5 方法Set.prototype.delete(value)
作用:删除某个值,返回一个布尔值,表示删除是否成功
let set=new Set([1,2,3,3]);
set.delete(2)
// true
set
// Set(2) {1,3}
2.6 方法Set.prototype.clear()
作用:清除所有set中保存的数据
set实例具有四个遍历的方法:keys(),values(),entries(),forEach()。
3.1 keys(),values(),entries()
keys()将set实例的所有键名作为SetIterator返回,values()将set实例的所有键值作为SetIterable返回。entries()同样返回SetIterable,在console.log时会将该实例此时的键名与键值打印出来。keys(),values(),entries()方法返回的SetIterable本质上是不同的,从打印结果就可以看出。由于Set结构数据类似于Array数据,key与value相同,因此keys()与values()返回的值完全相同,entries()返回的每个表示键值对的数组中的2个元素的数值同样相同。
let set=new Set([1,2,3,3]);
for(let i of set.keys()){
console.log(i);
}
//1
//2
//3
set.keys()
// SetIterator {1, 2, 3}
for (let i of set.entries()) {
console.log(i);
}
// ['1':'1']
// ['2':'2']
// ['3':'3']
set.entries()
// SetIterator {1, 2, 3}
由于set实例的默认遍历器生成函数是它的values()方法
Set.prototype[Symbol.iterator] === Set.prototype.values
// true
也就是说Set数据结构具有一个默认的iterator接口(该接口由它的values()方法生成的),因此可以对set实例直接进行for/of遍历,此处不再举例说明。
3.2 forEach()
类似于Array,Set结构也拥有forEach()方法,不过callback的参数与Array的不同。Array.prototype.forEach(callback(currentValue,index,array){}[,thisArg]),Set.prototype.forEach(callback(value,key){}[,thisArg]),当然了每次执行时的value与key是相同的。forEach()返回undefined,另外不会改变对象里的值。关于map、forEach的区别可以参考以下回答。
同样的,Array的map与filter方法也可用于Set类型数据。不过得先转成数组形式,可以参考1中的’将Set转为Array2种方法’,注意map和filter有返回值。
WeakSet与Set类型基本一致,值得注意的是WeakSet里边存放成员的只能是对象类型,例如
let weakset=new WeakSet();
weakset.add({}); // 正确
weakset.add([]); //正确
weakset.add(1); // 错误
另外WeakSet的成员对象都采用弱引用,垃圾回收机制可以回收WeakSet引用的对象所占用的内存。因此,WeakSet 适合临时存放一组对象,以及存放跟对象绑定的信息。只要这些对象在外部消失,它在 WeakSet 里面的引用就会自动消失。所以WeakSet的成员不适合引用,可能会被随时清理掉。另外也不可遍历,因此也没有size属性和forEach操作。
WeakSet具有add(),deldete(),has()方法,用法与Set一致,不过传的参数都是对象类型。
Map类型的出现是为了解决传统JavaScript对象中的键值对的键名只能是字符串,由此会带来一些不便。而Map数据类型类似于对象,也是键值对的集合,但键名可以是任何类型数据,包括Number,Object,Boolean等。
用法:
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false
在Map赋值时,若键名是Number,NaN(set时会认为NaN等于它本身)或Boolean,在多次赋同样键名的值会有替换效果。例如:
const map = new Map();
map
.set(1, 'aaa')
.set(1, 'bbb');
map.get(1) // "bbb"
若键名是字符串,则不会有此问题,因为字符串作键名会和内存地址绑定,同一字符的不同实例的内存地址不同。
const map = new Map();
const k1 = ['a'];
const k2 = ['a'];
map
.set(k1, 111)
.set(k2, 222);
map.get(k1) // 111
map.get(k2) // 222
但链式set由于指向统一内存地址,因此会有值替换效果。
const map = new Map();
map
.set('a', 'aaa')
.set('a', 'bbb');
map.get('a') // 'bbb'
Map数据结构同样有size属性,set(key,value),get(key),has(key),delete(key),clear()方法。
可以通过它的keys(),values(),entries(),或实例(等同于entries)提供的Iterable接口用for/of遍历,
const map = new Map([
['F', 'no'],
['T', 'yes'],
]);
for (let key of map.keys()) {
console.log(key);
}
// "F"
// "T"
for (let item of map.entries()) {
console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"
for (let [key, value] of map) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"
for (let item of map) {
console.log(item);
}
// ["F", "no"]
// ["T", "yes"]
也可以对实例直接forEach遍历。
map.forEach(function(value, key, map) {
console.log("Key: %s, Value: %s", key, value);
})
// Key: F, Value: no
// Key: T, Value: yes
——参考阮一峰《ES6入门》