产生起因
JavaScript的默认对象表示方式{}
可以视为其他语言中的Map
或Dictionary
的数据结构,即一组键值对。
但是JavaScript的对象有个小问题,就是键必须是字符串。但实际上Number或者其他数据类型作为键也是非常合理的。为了解决这个问题,最新的ES6规范引入了新的数据类型Map
和set
.
Set是一种叫做集合的数据结构,Map是一种叫做字典的数据结构
应用场景
数组去重和数据存储
Set (集合)[类数组]
-
集合是由一组无序且唯一(即不能重复)的项组成的,可以想象成集合是一个既没有重复元素,也没有顺序概念的数组
var a = new Set([1, 2, 3, {"1": "2"}, ["3","4"]])
-
ES6提供了新的数据结构Set。它类似于数组,但是成员的值都是唯一的,没有重复的值
-
Set 本身是一个构造函数,用来生成 Set 数据结构
这里说的Set其实就是我们所要讲到的集合,先来看下基础用法
const s = new Set(); [2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x)); for (let i of s) { console.log(i); // 2 3 5 4 } // 去除数组的重复成员 let array = [1,2,1,4,5,3]; [...new Set(array)] // [1, 2, 4, 5, 3]
方法和属性总结
-
属性
size: 返回set数据结构的数据长度 -
操作方法
-
add(value):添加某个值,返回 Set 结构本身。
-
delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
-
has(value):返回一个布尔值,表示该值是否为 Set 的成员。
-
clear():清除所有成员,无返回值。
let set = new Set(); console.log(set.add(1).add(2)); // Set [ 1, 2 ] console.log(set.delete(2)); // true console.log(set.has(2)); // false console.log(set.clear()); // undefined console.log(set.has(1)); // false console.log(set.size); // 0
-
-
遍历方法
-
keys():返回键名的遍历器
-
values():返回键值的遍历器
-
entries():返回键值对的遍历器
-
forEach():使用回调函数遍历每个成员,无返回值
let set = new Set(['a', 'b', 'c']); console.log(set.keys()); // SetIterator {"a", "b", "c"} console.log([...set.keys()]); // ["a", "b", "c"]
let set = new Set(['a', 'b', 'c']); console.log(set.values()); // SetIterator {"a", "b", "c"} console.log([...set.values()]); // ["a", "b", "c"]
let set = new Set(['a', 'b', 'c']); console.log(set.entries()); // SetIterator {"a", "b", "c"} console.log([...set.entries()]); // [["a", "a"], ["b", "b"], ["c", "c"]]
let set = new Set([1, 2, 3]); set.forEach((value, key) => console.log(key + ': ' + value)); // 1: 1 // 2: 2 // 3: 3
-
方法剖析(实现原理)
-
建一个集合
function Set(arr = []) { // 可以传入数组 let items = {}; this.size = 0; // 记录集合中成员的数量 } module.exports = Set;
这里用{}对象来表示集合,也是因为对象不允许一个键指向两个不同的属性,保证了集合里的元素都是唯一的
-
has方法
// has(val)方法 this.has = function (val) { // 对象都有hasOwnProperty方法,判断是否拥有特定属性 return items.hasOwnProperty(val); };
-
add方法
// add(val)方法 this.add = function (val) { if (!this.has(val)) { items[val] = val; this.size++; // 累加集合成员数量 return true; } return false; };
-
keys、values方法
// keys()方法 this.keys = function () { return Object.keys(items); // 返回遍历集合的所有键名的数组 }; // values()方法 this.values = function () { return Object.values(items); // 返回遍历集合的所有键值的数组 };
-
forEach方法
// forEach(fn, context)方法 this.forEach = function(fn, context = this) { for (let i = 0; i < this.size; i++) { let item = Object.keys(items)[i]; fn.call(context, item, item, items); } };
-
Array.from()
将Set类数组转化为真实的数组
let arrayLike = { 0: 'tom', 1: '65', 2: '男', 3: ['jane','john','Mary'], 'length': 4 } let arr = Array.from(arrayLike) console.log(arr) // ['tom','65','男',['jane','john','Mary']]
-
与Array之间的关系
-
var myArray = ["value1", "value2", "value3"]; // 用Set构造器将Array转换为Set var mySet = new Set(myArray); mySet.has("value1"); // returns true // 用...(展开操作符)操作符将Set转换为Array console.log([...mySet]); // 与myArray完全一致
-
高级用法
let set = new Set([2, 1, 3]); console.log(set.keys()); // [ '1', '2', '3' ] console.log(set.values()); // [ 1, 2, 3 ] console.log(set.size); // 3 set.delete(1); console.log(set.values()); // [ 2, 3 ] set.clear(); console.log(set.size); // 0 // 并集 let a = [1, 2, 3]; let b = new Set([4, 3, 2]); let union = new Set(a).union(b).values(); console.log(union); // [ 1, 2, 3, 4 ] //或者 let a = new Set([1, 2, 3, 4]) let b = new Set([4, 5, 6]) let union = new Set([...a, ...b]) console.log(union) //1 2 3 4 5 6 去掉重复的4 // 交集 let c = new Set([4, 3, 2]); let intersect = new Set([1,2,3]).intersect(c).values(); console.log(intersect); // [ 2, 3 ] //或者 let a = new Set([1, 2, 3, 4]) let b = new([4, 5, 6]) let difference = [...a].filter(function(ele) { return b.has(ele); }) // 4 //箭头函数 let difference = [...a].filter(x => x.has(x)) // 4 // 差集 let d = new Set([4, 3, 2]); let difference = new Set([1,2,3]).difference(d).values(); // [1,2,3]和[4,3,2]的差集是1 console.log(difference); // [ 1 ] //或者 let a = new Set([1, 2, 3, 4]) let b = new([4, 5, 6]) let difference = [...a].filter(function (ele) { return !b.has(ele); }) // 1, 2, 3
Map (字典)[类对象]
它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适;
那么集合又和字典有什么区别呢:
-
共同点:集合、字典可以存储不重复的值
-
不同点:集合是以[值,值]的形式存储元素,字典是以[键,值]的形式存储
这里说的Set其实就是我们所要讲到的集合,先来看下基础用法
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
方法和属性总结
-
属性
size:返回字典所包含的元素个数
-
操作方法
-
set(key, val): 向字典中添加新元素
-
get(key):通过键值查找特定的数值并返回
-
has(key):如果键存在字典中返回true,否则false
-
delete(key): 通过键值从字典中移除对应的数据
-
clear():将这个字典中的所有元素删除
-
let m = new Map(); m.set('Jay', 'Jay的Chou'); m.set(true, '真的'); console.log(m.has('Chou')); // false console.log(m.size); // 2 console.log(m.keys()); // [ 'Jay', 'true' ] console.log(m.values()); // [ 'Jay的Chou', '真的' ] console.log(m.get('jay')); // undefined m.delete(true); console.log(m.keys()); // [ 'Jay' ] console.log(m.values()); // [ 'Jay的Chou' ]
由于一个key只能对应一个value,所以,多次对一个key放入value,后面的值会把前面的值冲掉:
var m = new Map(); m.set('Adam', 67); m.set('Adam', 88); m.get('Adam'); // 88
Map方法接受一个二维数组当做参数
var m = new Map([['name', 'zhangsan'],['sex', 'male']]); console.log(m); //Map {"name" => "zhangsan", "sex" => "male"}
-
-
遍历方法
-
keys():将字典中包含的所有键名以数组形式返回
-
values():将字典中包含的所有数值以数组形式返回
-
entries():返回键值对的遍历器
-
forEach():遍历字典的所有成员
var map = new Map([['age', 19],['height', 180]]); for (let key of map.keys()) { console.log(key); // age height } for (let value of map.values()) { console.log(value); //19 180 } for (let item of map.entries()) { console.log(`${ item[0] }: ${ item[1] }`); //age: 19 height: 180 } for (let [key, value] of map.entries()) { console.log(`${ key }: ${ value }`); //age: 19 height: 180 } for (let [key, value] of map) { console.log(`${ key }: ${ value }`); //age: 19 height: 180 } map.forEach((value, key, map) => { console.log(`${ key }: ${ value }`); //age: 19 height: 180 });
-
方法剖析(实现原理)
-
建一个字典
function Map() { let items = {}; } module.exports = Map; // 导出
-
has方法
// has(key)方法 this.has = function (val) { return items.hasOwnProperty(val); };
-
set和get方法
// set(key, val)方法 // set相同key时,后面声明的会覆盖前面 // 如: new Map().set({}, 'a') this.set = function (key, val) { items[key] = val; }; // get(key)方法 this.get = function (key) { // 判断是否有key,如果有的话直接返回对应的值 // 如果读取一个未知的键,则返回undefined return this.has(key) ? items[key] : undefined; };
-
keys、values方法
// keys()方法 this.keys = function () { return Object.keys(items); // 返回遍历集合的所有键名的数组 }; // values()方法 this.values = function () { return Object.values(items); // 返回遍历集合的所有键值的数组 };
-
forEach方法
// forEach(fn, context)方法 this.forEach = function(fn, context = this) { for (let i = 0; i < this.size; i++) { let item = Object.keys(items)[i]; fn.call(context, element, sameElement, set); } };
-
与Array之间的关系
//Map转数组 const map = new Map(); map.set('name' , 'hello').set({},'world'); [...map] //[["name","hello"],[{},"world"]] [...map.value()] //[["hello","world"] //数组转Map const map = new Map([["name","hello"],[{},"world"]]); map // {"name" => "hello", Object {} => "world"}
-
与对象之间的关系
//Map转为对象的方法 function strMapToObj(strMap) { let obj = Object.create(null); for (let [k,v] of strMap) { obj[k] = v; } return obj; } let myMap = new Map().set('name', 'Virgo').set('old', '18'); console.log(strMapToObj(myMap));// { name: "Virgo", old: "18"} //对象转为Map的方法 function objToStrMap(obj) { let strMap = new Map(); for (let k of Object.keys(obj)) { strMap.set(k, obj[k]); } return strMap; } var a = {name: 'Virgo', old: '18'} console.log(objToStrMap(a))//Map(2){"name" => "Virgo", "old" => "18"}