ES6之前,数组一直是javascript中唯一的集合类型,而作为唯一的集合类型,数组使用的索引又是数值类型。因此,为使用非数值型索引的数据结构,ES6新增了Set集合和Map集合,两种新的集合类型。上一篇文章笔者已经回顾了数组对象及其方法,这篇文章依然是结合MDN的内容来回顾一下ES6中的Set和Map对象。
1.Set集合
ES6中新增的Set集合是一种有序列表,其中含有相互独立的非重复的值。MDN中Set的描述如下:Set对象是值的集合,你可以按照插入的顺序迭代它的元素,Set中的元素是唯一的。Set对象的构造函数为Set(),构造函数的属性有:
- get Set[Symbol.species]:访问器属性,返回Set的构造函数,其被构造函数用以创建派生对象
- protoType:表示构造函数的原型对象.
所有Set对象的创建都是通过Set()构造函数来创建的。
// 1.Set()构造函数创建,add()添加元素
const set = new Set();
set.add(1);
set.add(2);
set.add(3);
console.log(set); // Set(3) {1, 2, 3}
// 2.一维数组
const set1 = new Set([1,2,3,4,5]);
console.log(set1); // Set(5) {1, 2, 3, 4, 5}
// 3.string字符串
const text = 'Winter';
const set2 = new Set(text);
console.log(set2); // Set(6) {'W', 'i', 'n', 't', 'e', 'r'}
// 4.可迭代对象[Symbol.iterator]
const obj = {
[Symbol.iterator]: function* () {
yield 1;
yield 2;
yield 3;
}
}
const set2 = new Set(obj);
console.log(set2); // Set(3) {1, 2, 3}
/* 应用场景 */
// 1.Set转数组
const set = new Set();
set.add(1).add(2).add(3).add(4).add(5);
const arr = [...set];
console.log(arr); // [1, 2, 3, 4, 5]
// 2.数组去重
const arr = [1,1,2,2,3,3,4,5];
console.log([...new Set(arr)]); // [1, 2, 3, 4, 5]
所有Set实例都会继承Set.protoType,Set实例的属性有:
- size:表示Set对象中的元素个数
- constructor:表示创建该实例的构造函数
Set对象实例的方法包括对象元素的操作方法和遍历方法。操作方法有:add(), delete(), clear(), has();遍历方法有:entries(), keys(), values(), forEach()。
1.1 操作方法
-
add():向Set对象的末尾添加一个指定的值。
语法:mySet.add(value)
value必须,为添加到Set集合中的元素的值,元素的类型没有限制,但元素不能重复添加,NaN之间被视为相同的值,虽然NaN !== NaN。add()方法支持链式调用。
// 支持链式调用,元素类型没有限制
const mySet = new Set();
mySet.add(1);
mySet.add(2).add(true).add('hello').add(undefined).add(null).add({}).add(NaN);
console.log(mySet); // Set(8) {1, 2, true, "hello", undefined, null, {}, NaN}
// NaN被视为相同的值,不会重复添加
mySet.add(Number('hello'));
console.log(mySet); // Set(8) {1, 2, true, "hello", undefined, null, {}, NaN}
-
delete():从Set对象中删除指定的元素。
语法:mySet.delete(value)
value必需,为将要删除的元素。成功删除返回true,否则返回false。
const mySet = new Set([1,2,3,4,5]);
console.log(mySet.delete(3)); // true
console.log(mySet.delete(6)); // false
console.log(mySet); // Set(4) {1, 2, 4, 5}
-
has():返回一个boolean值来指示对应的值value是否存在于Set对象中。
语法:mySet.has(value)
value必需,为测试是否存在于Set对象中的值。若value存在于Set中返回true,否则返回false。
const set1 = new Set();
set1.add(1).add(2).add(3).add(4).add(5);
console.log(set1.has(3)); // true
console.log(set1.has(6)); // false
const set2 = new Set();
const obj = {key: 1};
set2.add(obj);
console.log(set2.has(obj)); // true
console.log(set2.has({key:1})); // false,因为此时的{key:1}是另一个对象的引用
set2.add({key:1}); // 现在set2中有两条不同引用的对象了
console.log(set2); // Set(2) {{key:1}, {key:1}}
-
clear():清空一个Set对象中的所有元素。返回值为undefined。
语法:mySet.clear()
const mySet = new Set();
mySet.add(1).add(2).add(3).add(4).add(5);
mySet.clear();
console.log(mySet.size); // 0
console.log(mySet.has(3)); // false
1.2 内置迭代器
上一篇文章讲到,ES6中的3种类型的集合对象:数组、Set集合和Map集合,都内置了三种迭代器:entries(), values(), keys()。
-
entries():返回一个迭代器对象,对象的元素是类似[value,value]形式的数组,value是Set集合的每个元素,迭代器顺序即元素插入的顺序。
语法:mySet.entries()
返回值为一个新的包含[value,value]形式的数组迭代器元素。每次调用next()方法,都会返回一个结果对象,结果对象的value为一个数组,数组的两个元素都是Set集合对象的值。
const mySet = new Set();
mySet.add(1).add(2).add(3);
const iter = mySet.entries();
console.log(iter.next()); // {value: [1,1], done: false}
console.log(iter.next()); // {value: [2,2], done: false}
console.log(iter.next()); // {value: [3,3], done: false}
console.log(iter.next()); // {value: undefined, done: true}
-
values():返回一个迭代器对象,对象的元素为集合的元素。
语法:mySet.values()
返回值为按照插入顺序返回的包含Set对象中的每个元素的迭代器对象。每次调用next()方法,返回的结果对象的value为Set集合对象的值。
const mySet = new Set([1,2,3]);
const iter = mySet.values();
console.log(iter.next()); // {value: 1, done: false}
console.log(iter.next()); // {value: 2, done: false}
console.log(iter.next()); // {value: 3, done: false}
console.log(iter.next()); // {value: undefined, done: true}
-
keys():返回和values()相同的迭代器对象,因为Set集合的键和值是相同的。
语法:mySet.keys()
const mySet = new Set([1,2,3]);
const iter = mySet.keys();
console.log(iter.next()); // {value: 1, done: false}
console.log(iter.next()); // {value: 2, done: false}
console.log(iter.next()); // {value: 3, done: false}
console.log(iter.next()); // {value: undefined, done: true}
Set集合的默认迭代器为values(),在for-of循环中,若没有显式指定迭代器则使用默认迭代器values()。
1.3 遍历方法
-
forEach():根据集合中元素的插入顺序,依次遍历元素执行提供的回调函数。
语法:mySet.forEach(callback(value,key,set),thisArg)
其中,callback为遍历每个元素执行的回调函数,该函数接收三个参数:value为当前遍历的元素,key也表示当前遍历的元素,set为调用forEach的集合对象。thisArg为执行回调函数时的this对象。
const mySet = new Set([1,2,3]);
mySet.forEach((value,key) => {
console.log(`mySet[${key}] = ${value}`);
})
// 'mySet[1] = 1'
// 'mySet[2] = 2'
// 'mySet[3] = 3'
2.WeakSet集合
我们知道,Set集合的元素类型不限。当我们将对象存储到Set的实例后,若Set实例中的引用存在,垃圾回收机制就不能释放该对象的内存空间,即使清除对象的原始引用,Set集合却保留了该对象的引用。(这里可以了解一下j s的垃圾回收机制:标记清除和引用计数。V8引擎采用的是分代式垃圾回收机制,年轻代采用复制清除算法,老年代采用标记清除算法。)
const set = new Set();
let obj = {name: 'zhang', age: 18};
set.add(obj);
console.log(set.size); // 1
// 移除对象的原始引用
obj = null;
console.log(set.size); // 1
// 取回原始引用
obj = [...set][0];
console.log(obj); // {name: 'zhang', age: 18}
由此可见,Set集合存储对象可能会导致内存泄露的问题。为了解决这个问题,ES6引入了又一个类型:WeakSet集合(弱引用Set集合)。WeakSet集合只存储对象的弱引用,并且不可以存储原始类型的值,若集合中的弱引用式对象的唯一引用则会被回收并释放相应内存。
WeakSet集合通过WeakSet()构造函数来创建,WeakSet对象实例没有size属性和clear()方法,只有constructor属性。集合支持3个方法:add()、has()和delete()。因为WeakSet实例的项是动态的,所以不能被遍历(没有遍历方法)。
// WeakSet集合的创建
const set = new WeakSet();
let obj = {name: 'zhang', age: 18};
// add()方法向集合中添加对象,只能添加对象,不能添加原始值
set.add(obj);
console.log(set.has(obj)); // true
set.delete(obj);
console.log(set.has(obj)); // false
Set与WeakSet最大的区别就是WeakSet保存的是对象的弱引用,且WeakSet只能保存对象,不能保存非对象值。
3.Map集合
Map对象保存键值对,并且能够记住键的原始插入顺序,任何值都可以作为一个键或一个值。Map对象在迭代时会根据对象中元素插入的顺序来进行。Map对象的构造函数为Map(),构造函数的属性有:
- get Map[Symbol.species]:可访问属性,用以创建派生对象。
- protoType:表示Map构造函数的原型。允许添加属性到原型上从而应用于所有的Map对象。
Map对象的创建都是通过Map()构造函数来创建的。
// 1.Map()构造函数创建,set()方法添加元素
const map = new Map();
map.set(1,'red');
map.set(2, 'yellow');
map.set(3, 'blue');
console.log(map); // Map(3) {1 => 'red', 2 => 'yellow', 3 => 'blue'}
// 2.二维数组
const map = new Map([[1, 'red'],[2, 'yellow'], [3, 'blue']]);
console.log(map); // Map(3) {1 => 'red', 2 => 'yellow', 3 => 'blue'}
Map对象实例都会继承Map.protoType,Map实例的属性有:
- constructor:表示创建实例的构造函数
- size:表示实例对象的键值对的数量
Map对象实例的方法包括对象元素的操作方法和遍历方法。操作方法:set(), delete(), clear(), get(), has();遍历方法:entries(), keys(), values(), forEach()。
3.1 操作方法
-
set():为Map对象添加或更新一个指定键和值的键值对。若键已存在则更新键对应的值。
语法:map.set(key, value)
key为要添加或更新Map对象的元素的键,value为要添加或更新Map对象的元素的值。key和value的值支持所有类型。键名的等价性判断是通过Object,is()方法实现的。NaN被视为相同的键。
const map = new Map();
// 添加元素
map.set(1, 'red');
map.set('yellow',2);
map.set(undefined,null);
console.log(map); // Map(3) {1 => "red", "yellow" => 2, undefined => null}
// 更新元素
map.set(1,'blue');
map.set('yellow',3);
map.set(undefined,undefined);
console.log(map); // Map(3) {1 => "blue", "yellow" => 3, undefined => undefined}
-
delete():移除Map对象中指定的元素
语法:map.delete(key)
key必须,为从Map对象中移除的元素的键。若Map对象中存在该元素,返回true;否则返回false。
const map = new Map();
map.set(1,'red');
map.set(2,'yellow');
map.set(3,'blue');
console.log(map.delete(1)); // true
console.log(map.delete(6)); // false
console.log(map); // Map(2) {2 => "yellow", 3 => "blue"}
-
get():获取Map对象中的指定键对应的元素。
语法:map.get(key)
key必须,为从目标Map对象中获取元素的键,返回值为Map对象中与指定键对应的值,若找不到该键则返回undefined。
const map = new Map([[1,'red'],[2,'yellow'],[3,'blue']]);
console.log(map.get(1)); // 'red'
console.log(map.get(6)); // undefined
-
has():查找Map对象中是否存在指定元素,返回一个boolean值。
语法:map.has(key)
key必须,为检测是否存在指定元素的键值。若Map对象中存在则返回true,否则返回false。
const map = new Map([['name','zhang'],['age',18],['sex','male']]);
console.log(map.has('name')); // true
console.log(map.has('greet')); // false
-
clear():移除Map对象中的所有元素。
语法:map.clear()
const map = new Map([['name','zhang'],['age',18],['sex','male']]);
console.log(map.size); // 3
console.log(map.has('name')); // true
map.clear();
console.log(map.size); // 0
console.log(map.has('name')); // false
3.2 内置迭代器
Map集合对象同样内置了三种迭代器:entries(), values(), keys()。
-
entries():返回一个新的包含[key, value]对的迭代器对象,迭代顺序为Map对象的元素插入顺序。
语法:map.entries()
返回值为一个新的包含[key, value]形式的数组迭代器元素。每次调用next()方法,都会返回一个结果对象,结果对象的value为一个数组,数组的两个元素都是Map集合对象的键值对。
const map = new Map([[1, 'red'], [2, 'yellow'], [3, 'blue']]);
const iter = map.entries();
console.log(iter.next()); // {value: [1, 'red'], done: false}
console.log(iter.next()); // {value: [2, 'yellow'], done: false}
console.log(iter.next()); // {value: [3, 'blue'], done: false}
console.log(iter.next()); // {value: undifined, done: true}
-
values():返回一个迭代器对象,对象的元素为集合的元素的值。
语法:map.values()
返回值为按插入顺序返回的包含Map对象中的每个元素的值的迭代器对象。每次调用next()方法,返回的结果对象的value为Map集合对象的值。
const map = new Map([[1, 'red'],[2, 'yellow'],[3, 'blue']]);
const iter = map.values();
console.log(iter.next()); // {value: 'red', done: false}
console.log(iter.next()); // {value: 'yellow', done: false}
console.log(iter.next()); // {value: 'blue', done: false}
console.log(iter.next()); // {value: undifined, done: true}
-
keys():返回一个迭代器对象,对象的元素为集合的元素的键。
语法:map.keys()
返回值为按插入顺序返回的包含Map对象中的每个元素的键的迭代器对象。每次调用next()方法,返回的结果对象的value为Map集合对象的键。
const map = new Map([[1, 'red'],[2, 'yellow'],[3, 'blue']]);
const iter = map.keys();
console.log(iter.next()); // {value: 1, done: false}
console.log(iter.next()); // {value: 2, done: false}
console.log(iter.next()); // {value: 3, done: false}
console.log(iter.next()); // {value: undifined, done: true}
Map集合的默认迭代器为entries(),在for-of循环中,若没有显式指定迭代器则使用默认迭代器entries()。
3.3 遍历方法
-
forEach():按集合元素的插入顺序,遍历对象的每个元素执行指定的回调函数。
语法:map.forEach(callback(value,key,map),thisArg)
其中,callback为遍历每个元素执行的回调函数,该函数接收三个参数:value为当前遍历的元素,key也表示当前遍历的元素,map为调用forEach的集合对象。thisArg为执行回调函数时的this对象。
const map = new Map([[1, 'red'],[2, 'yellow'],[3, 'blue']]);
map.forEach((value,key) => {
console.log(`map[${key}] = ${value}`);
})
// 'map[1] = red'
// 'map[2] = yellow'
// 'map[3] = blue'
4.WeakMap集合
WeakMap集合(弱引用的Map集合)同样是为了解决Map存储对象的强引用问题而设计出来的。WeakMap集合只存储对象的弱引用,并且键名不可以存储原始类型的值,若集合中的弱引用式对象的唯一引用则会被回收并释放相应内存。Map与WeakMap最大的区别就是WeakMap保存的是对象的弱引用,且WeakMap的键只能为对象,不能为非对象值。
const map = new Map();
let obj = {name: 'zhang', age: 18};
map.set(obj, 1);
console.log(map); // Map(1) {{name: 'zhang', age: 18} => 1}
// 移除对象的原始引用
obj = null;
console.log(map); // Map(1) {{name: 'zhang', age: 18} => 1}
// 取回原始引用
obj = map.keys().next().value;
console.log(obj); // {name: 'zhang', age: 18}
WeakMap集合通过WeakMap()构造函数来创建,WeakMap对象实例没有size属性和clear()方法,只有constructor属性。集合支持4个方法:set()、get()、has()和delete()。因为WeakMap实例的项是动态的,所以不能被遍历(没有遍历方法)。
const map = new Map();
let obj = {name: 'zhang', age: 18};
// set添加或更新元素,键必须是对象
map.set(obj, 1);
console.log(map.has(obj)); // true
console.log(map.get(obj)); // 1
map.delete(obj);
console.log(map.has(obj)); // false
【参考文献】
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!