数据类型

Map&Set

Map

  1. var m = {};
  2. var x = { id: 1 }
  3. var y = { id: 2 };
  4. m[x] = 'foo';
  5. m[y] = 'bar';
  6. //这里看出{id:1}转为字符串[object Object]
  7. console.log(Object.prototype.toString({ id: 1 })); //[object Object]
  8. console.log(m); //{[object Object]: "bar"}
  9. //一样的键名,第二次会覆盖第一次的操作
  10. console.log(m[x]); //bar
  11. console.log(m[y]); //bar

以上可以看出传统的对象处理方式并不友好,当键名是一个对象的时候,它是调用原型上的toString()方法,转完之后并不能实现键名键值一一对应,ES6提供的新的数据结构Map()来解决以上问题

Map()存放的是对象,有键名和键值,且键名不限于字符串,也可以是对象,也可实现一一对应的关系

  1. let m = new Map();
  2. var x = { id: 1 };
  3. var y = { id: 2 };
  4. //设置键名和键值
  5. m.set(x, 'foo');
  6. m.set(y, 'bar');
  7. //获取键值
  8. console.log(m.get(x)); //foo
  9. console.log(m.get(y)); //bar
  10. console.log(m);
  11. /**
  12. * Map(2) {{…} => "foo", {…} => "bar"}
  13. * [[Entries]]
  14. * 0: {Object => "foo"}
  15. * 1: {Object => "bar"}
  16. * size: 2
  17. * __proto__: Map
  18. */

Map()数据也具备iterator接口的数据结构

  1. //写法一:
  2. //参数必须以数组的形式出现双源的数据结构
  3. let m = new Map([
  4. ['name', 'zhangsan'],
  5. ['title', 'lisi']
  6. ]);
  7. //写法二:
  8. let m2 = new Map();
  9. m2.set('name', 'zhangsan');
  10. m2.set('title', 'lisi');
  11. console.log(m);
  12. /**
  13. * Map(2) {"name" => "zhangsan", "title" => "lisi"}
  14. * [[Entries]]
  15. * 0: {"name" => "zhangsan"}
  16. * 1: {"title" => "lisi"}
  17. * size: 2
  18. * __proto__: Map
  19. */
  1. //模拟将键名和键值遍历到map数据里
  2. var items = [
  3. ['name', 'wangwu'],
  4. ['title', 'zhaoliu']
  5. ];
  6. let m = new Map();
  7. items.forEach(([key, value]) => m.set(key, value));
  8. console.log(m);
  9. /**
  10. * Map(2) {"name" => "wangwu", "title" => "zhaoliu"}
  11. * [[Entries]]
  12. * 0: {"name" => "wangwu"}
  13. * key: "name"
  14. * value: "wangwu"
  15. * 1: {"title" => "zhaoliu"}
  16. * key: "title"
  17. * value: "zhaoliu"
  18. * size: 2
  19. * __proto__: Map
  20. */
  1. //键值相同覆盖的问题
  2. const map = new Map();
  3. map.set(1, 'foo');
  4. map.set(1, 'bar');
  5. console.log(map.get(1)); //bar
  1. //键值为NaN
  2. const map = new Map();
  3. map.set(NaN, 123);
  4. console.log(map.get(NaN)); //123
  5. console.log(NaN === NaN); //false
  6. console.log(Object.is(NaN, NaN)); //true
  1. //如何将Map数据转为数组
  2. const myMap = new Map();
  3. //设置键名键值
  4. myMap.set(true, 7);
  5. myMap.set({
  6. foo: 3
  7. }, ['abc']);
  8. console.log(myMap);
  9. //Map(2) {true => 7, {foo: 3} => ['abc']}
  10. console.log([...myMap]);
  11. //[[true, 7], [{foo: 3}, ["abc"]]]
  1. //如何将Map数据转为对象
  2. const myMap = new Map();
  3. myMap.set(true, 7);
  4. myMap.set('a', 'abc');
  5. function strMapToObj(strMap) {
  6. //Object.create(指向原型)
  7. let obj = Object.create(null);
  8. //迭代键名键值
  9. for (let [key, val] of strMap.entries()) {
  10. obj[key] = val;
  11. }
  12. return obj;
  13. }
  14. console.log(strMapToObj(myMap));
  15. //{true: 7, a: "abc"}
  1. //如何将对象转为Map数据
  2. function objToStrMap(obj) {
  3. let strMap = new Map();
  4. for (let key in obj) {
  5. strMap.set(key, obj[key])
  6. }
  7. return strMap;
  8. }
  9. console.log(objToStrMap({
  10. true: 7,
  11. 'no': false
  12. }));
  13. //Map(2) {"true" => 7, "no" => false}

Set

特点:成员唯一的数组

场景:利用set的唯一性对数据进行去重,然后将set转化为数组提高效率

  1. //参数只能是具备iterator接口的数据结构
  2. var set = new Set([1, 2, 3, 4, 5]);
  3. set.add(5);
  4. set.add(7);
  5. console.log(set);
  6. /**
  7. * Set(6) {1, 2, 3, 4, 5, …}
  8. * [[Entries]]
  9. * 0: 1
  10. * 1: 2
  11. * 2: 3
  12. * 3: 4
  13. * 4: 5
  14. * 5: 7
  15. * size: (...)
  16. * __proto__: Set
  17. */
  1. //简单判断特殊的值让set识别是否唯一的
  2. var set = new Set([undefined, undefined, null, null, 5, '5', true, 1, NaN, NaN, {}, {}, [], []]);
  3. console.log(set);
  4. /**
  5. * Set(11) {undefined, null, 5, "5", true, …}
  6. * [[Entries]]
  7. * 0: undefined
  8. * 1: null
  9. * 2: 5
  10. * 3: "5"
  11. * 4: true
  12. * 5: 1
  13. * 6: NaN
  14. * 7: Object
  15. * 8: Object
  16. * 9: Array(0)
  17. * 10: Array(0)
  18. * size: (...)
  19. * __proto__: Set
  20. */

方法:add()/delete()/clear()/has()

  1. //add()添加值,返回的是set结构本身
  2. var set = new Set();
  3. var x = {
  4. id: 1
  5. },
  6. y = {
  7. id: 2
  8. }
  9. set.add(x);
  10. set.add(y);
  11. console.log(set);
  12. /**
  13. * Set(2) {{…}, {…}}
  14. * [[Entries]]
  15. * 0:
  16. * value: {id: 1}
  17. * 1:
  18. * value: {id: 2}
  19. * size: 2
  20. * __proto__: Set
  21. */
  1. //delete()删除指定一个
  2. //返回值为布尔值 true可删/false没数据可删
  3. console.log(set.delete(y));
  4. /**
  5. * Set(1) {{…}}
  6. * [[Entries]]
  7. * 0:
  8. * value: {id: 1}
  9. * size: 1
  10. * __proto__: Set
  11. */
  1. //clear()全部清空
  2. //返回值为undefined
  3. console.log(set.clear());
  4. /**
  5. * Set(0) {}
  6. * [[Entries]]
  7. * No properties
  8. * size: 0
  9. * __proto__: Set
  10. */
  1. //has()判断当前是否含有指定值
  2. //返回值为布尔值 true有该值/false没有该值
  3. console.log(set.has(x));
  1. //set特征:实时监控数据
  2. var set = new Set([1, 2]);
  3. console.log(set);
  4. set.clear();
  5. //注意:打印是在清除之前的,但仍然没有数据显示,说明set实时监控数据
  6. /**
  7. * Set(2) {1, 2}
  8. * [[Entries]]
  9. * No properties
  10. * size: 0
  11. * __proto__: Set
  12. */

遍历方法:keys()/values()/entries()/forEach()

  1. //返回值是迭代器对象
  2. let set = new Set([1, 2, 3]);
  3. console.log(set.keys());
  4. console.log(set.values());
  5. console.log(set.entries());
  6. /**
  7. * SetIterator {1, 2, 3}
  8. * [[Entries]]
  9. * 0: 1
  10. * 1: 2
  11. * 2: 3
  12. * __proto__: Set Iterator
  13. * [[IteratorHasMore]]: true
  14. * [[IteratorIndex]]: 0
  15. * [[IteratorKind]]: "values"
  16. */
  17. /**
  18. * SetIterator {1, 2, 3}
  19. * [[Entries]]
  20. * 0: 1
  21. * 1: 2
  22. * 2: 3
  23. * __proto__: Set Iterator
  24. * [[IteratorHasMore]]: true
  25. * [[IteratorIndex]]: 0
  26. * [[IteratorKind]]: "values"
  27. */
  28. /**
  29. * SetIterator {1 => 1, 2 => 2, 3 => 3}
  30. * [[Entries]]
  31. * 0: {1 => 1}
  32. * 1: {2 => 2}
  33. * 2: {3 => 3}
  34. * __proto__: Set Iterator
  35. * [[IteratorHasMore]]: true
  36. * [[IteratorIndex]]: 0
  37. * [[IteratorKind]]: "entries"
  38. */

for of循环迭代器对象

  1. let set = new Set([1, 2, 3]);
  2. //set结构没有键名
  3. //键名和键值是一致的
  4. for (let i of set.keys()) {
  5. console.log(i);
  6. //1 2 3
  7. }
  8. for (let i of set.values()) {
  9. console.log(i);
  10. //1 2 3
  11. }
  12. for (let i of set.entries()) {
  13. console.log(i);
  14. //[1, 1] [2, 2] [3, 3]
  15. }

单独遍历具有迭代器接口的set

  1. for (let i of set) {
  2. console.log(i);
  3. //1 2 3
  4. }
  5. //验证是返回值从哪个接口返回出来的? set.keys()或set.values()
  6. console.log(Set.prototype[Symbol.iterator] === Set.prototype.values); //true
  7. console.log(Set.prototype[Symbol.iterator] === Set.prototype.keys); //true
  8. //说明set依赖set.values()方法遍历出来的值
  1. //forEach()遍历
  2. let set = new Set([1, 2, 3]);
  3. set.forEach(function (value, keys, arr) {
  4. console.log(value); //1 1 Set{1, 2, 3}
  5. console.log(keys); //2 2 Set{1, 2, 3}
  6. console.log(arr); //3 3 Set{1, 2, 3}
  7. });

set适用展开运算符

  1. //利用展开运算符可以将set数据轻松转为数组结构
  2. let set = new Set([1, 2, 3]);
  3. console.log(...set); //1 2 3
  4. console.log([...set]); //[1, 2, 3]
  1. //将set数据里的数值乘以2
  2. //利用数组map()方法映射
  3. let set = new Set([1, 2, 3]);
  4. console.log([...set].map(value => value * 2));
  5. //[2, 4, 6]
  6. //写法二:利用Array.from()
  7. console.log(Array.from(set, value => value * 2));
  1. //set+filter过滤数组里的数值
  2. //返回偶数组合的数组
  3. let set = new Set([1, 2, 3, 4, 5, 6]);
  4. console.log([...set].filter(x => (x % 2) == 0));
  5. //[2, 4, 6]
  1. //交集 并集 差集
  2. let a = new Set([1, 2, 3]);
  3. let b = new Set([4, 3, 2]);
  4. //并集 去重
  5. let union = new Set([...a, ...b]);
  6. console.log(union); //Set(4) {1, 2, 3, 4}
  7. //交集 过滤掉b 有b里的值就通过
  8. let intersect = new Set([...a].filter(x => b.has(x)));
  9. console.log(intersect); //Set(2) {2, 3}
  10. //差集
  11. let difference = new Set([...a].filter(x => !b.has(x)));
  12. console.log(difference); //Set(1) {1}

数据结构对比

  1. //map & array 对比
  2. let map = new Map();
  3. let array = new Array();
  4. //增
  5. map.set('t', 1);
  6. array.push({
  7. 't': 1
  8. });
  9. console.log(map); //Map(1) {"t" => 1}
  10. console.log(array); //[{t: 1}]
  11. //查
  12. console.log(map.has('t')); //true
  13. console.log(array.find(val => val['t'])); //{t: 1}
  14. //改
  15. map.set('t', 2)
  16. console.log(map);
  17. array.forEach(item => item.t ? item.t = 2 : '');
  18. console.log(array); //[{t: 2}]
  19. //删
  20. map.delete('t');
  21. console.log(map); //Map(0) {}
  22. let index = array.findIndex(item => item.t);
  23. array.splice(index, 1);
  24. console.log(array); //[]
  25. //总结:map要更方便些
  1. //set & array 对比
  2. let set = new Set();
  3. let array = [];
  4. //把引用值存起来
  5. let obj = {
  6. t: 1
  7. };
  8. //增
  9. set.add(obj);
  10. array.push({
  11. 't': 1
  12. });
  13. console.log(set); //{[{t: 1}]}
  14. console.log(array); //[{t: 1}]
  15. //查
  16. console.log(set.has(obj)); //true
  17. console.log(array.find(val => val['t'])); //{t: 1}
  18. //改
  19. set.forEach(item => item.t ? item.t = 2 : '');
  20. console.log(set); //{[{t: 2}]}
  21. array.forEach(item => item.t ? item.t = 2 : '');
  22. console.log(array); //[{t: 2}]
  23. //删
  24. set.forEach(item => item.t ? set.delete(item) : '');
  25. console.log(set); //{}
  26. let index = array.findIndex(item => item.t);
  27. array.splice(index, 1);
  28. console.log(array); //[]
  29. //总结:操作方式set比array稍微略简
  1. //map set object 横向对比
  2. let item = {
  3. t: 1
  4. };
  5. let map = new Map();
  6. let set = new Set();
  7. let obj = {};
  8. //增
  9. map.set('t', 1);
  10. set.add(item);
  11. obj['t'] = 1;
  12. console.log(map, set, obj);
  13. //{"t" => 1}
  14. //{{0:{t: 1}}}
  15. //{t: 1}
  16. //查
  17. console.log(map.has('t')); //true
  18. console.log(set.has(item)); //true
  19. console.log('t' in obj); //true
  20. //改
  21. map.set('t', 2);
  22. set.forEach(item => item.t ? item.t = 2 : '');
  23. obj['t'] = 2;
  24. console.log(map, set, obj);
  25. //{"t" => 2}
  26. //{{0:{t: 2}}}
  27. //{t: 2}
  28. //删
  29. map.delete('t');
  30. set.delete(item);
  31. delete obj['t'];
  32. console.log(map, set, obj);
  33. //{} {} {}
  34. //总结:
  35. //1.map,set底层优化比obj,arr要好些
  36. //2.map,set操作方式比obj,arr更优雅
  37. //建议:
  38. //1.优先使用map
  39. //2.对数据有唯一性要求的用set

WeakMap&WeakSet

阉割版的MapSet

具有方法:add()/delete()/has()

没有遍历方法

垃圾回收机制不考虑他们俩的引用

WeekMapMap有什么区别?

  • Map的键名可以是任意类型
  • 普通对象的键名只能是字符串
  • WeekMap的键名只能是对象

注: WeekMap的键名是弱引用对象,只要引用没有了,关联的键值也会一并被垃圾回收

应用场景:

  1. 对于一些属性或方法数据用完时或绑定事件处理函数想销毁时,不需要手动销毁,利用WeekMap就可以处理销毁
  2. 在深拷贝中,利用WeekMap可以实现记录已经被拷贝过的对象或数组,来避免重复拷贝导致内存泄漏,浏览器死循环报错等问题
  1. //WeakSet&WeakMap成员只能是对象不能为别的数据,否则报错
  2. console.log(new WeakSet().add(1));