1. Set数据结构:它类似于数组,但是成员的值都是唯一的,没有重复的值。Set 本身是一个构造函数,用来生成 Set 数据结构。Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。
操作方法:
add(value)
:添加某个值,返回 Set 结构本身。delete(value)
:删除某个值,返回一个布尔值,表示删除是否成功。has(value)
:返回一个布尔值,表示该值是否为Set
的成员。clear()
:清除所有成员,没有返回值。s.add(1).add(2).add(2);// 注意2被加入了两次 s.size // 2 s.has(1) // true s.has(2) // true s.has(3) // false s.delete(2); s.has(2) // false
//Array.from方法可以将 Set 结构转为数组。 const items = new Set([1, 2, 3, 4, 5]); const array = Array.from(items); //这就提供了去除数组重复成员的另一种方法。 function dedupe(array) { return Array.from(new Set(array)); } dedupe([1, 1, 2, 3]) // [1, 2, 3]
遍历方法:
keys()
:返回键名的遍历器values()
:返回键值的遍历器entries()
:返回键值对的遍历器forEach()
:使用回调函数遍历每个成员//由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。 for (let item of set.values()) { console.log(item); } // red // green // blue for (let item of set.entries()) { console.log(item); } // ["red", "red"] // ["green", "green"] // ["blue", "blue"] let set = new Set(['red', 'green', 'blue']); for (let x of set) { console.log(x); } // red // green // blue //Set 结构的实例默认可遍历,它的默认遍历器生成函数就是它的values方法。 Set.prototype[Symbol.iterator] === Set.prototype.values // true let set = new Set(['red', 'green', 'blue']); for (let x of set) { console.log(x); } // red // green // blue
遍历的应用:
let arr = [3, 5, 2, 2, 5, 5]; let unique = [...new Set(arr)]; // [3, 5, 2] let set = new Set([1, 2, 3]); set = new Set([...set].map(x => x * 2)); // 返回Set结构:{2, 4, 6} let set = new Set([1, 2, 3, 4, 5]); set = new Set([...set].filter(x => (x % 2) == 0)); // 返回Set结构:{2, 4} //使用 Set 可以很容易地实现并集(Union)、交集(Intersect)和差集 let a = new Set([1, 2, 3]); let b = new Set([4, 3, 2]); // 并集 let union = new Set([...a, ...b]); // Set {1, 2, 3, 4} // 交集 let intersect = new Set([...a].filter(x => b.has(x))); // set {2, 3} // 差集 let difference = new Set([...a].filter(x => !b.has(x))); // Set {1}
2.Map:类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map 结构的实例有以下属性和操作方法。
//size属性返回 Map 结构的成员总数。 const map = new Map(); map.set('foo', true); map.set('bar', false); map.size // 2 //set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。 const m = new Map(); m.set('edition', 6) // 键是字符串 m.set(262, 'standard') // 键是数值 m.set(undefined, 'nah') // 键是 undefined //set方法返回的是当前的Map对象,因此可以采用链式写法。 let map = new Map() .set(1, 'a') .set(2, 'b') .set(3, 'c'); //get方法读取key对应的键值,如果找不到key,返回undefined。 const m = new Map(); const hello = function() {console.log('hello');}; m.set(hello, 'Hello ES6!') // 键是函数 m.get(hello) // Hello ES6! //has,delete,clear()方法: const m = new Map(); m.set(undefined, 'nah'); m.has(undefined) // true m.delete(undefined) m.has(undefined) // false
Map 结构原生提供三个遍历器生成函数和一个遍历方法。
//Map 结构的默认遍历器接口(Symbol.iterator属性),就是entries方法。 const map = new Map([ ['F', 'no'], ['T', 'yes'], ]); for (let [key, value] of map.entries()) { console.log(key, value); } // "F" "no" // "T" "yes" // 等同于使用map.entries() for (let [key, value] of map) { console.log(key, value); } // "F" "no" // "T" "yes"
Map 与其他数据结构的互换:
//Map 转为数组:扩展运算符(...)。 const myMap = new Map() .set(true, 7) .set({foo: 3}, ['abc']); [...myMap] // [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ] //数组 转为 Map new Map([ [true, 7], [{foo: 3}, ['abc']] ])// Map { // true => 7, // Object {foo: 3} => ['abc'] // }
3.WeakMap:解决内存泄漏
//WeakMap 应用的典型场合: 1.DOM 节点作为键名,2.部署私有属性 let myElement = document.getElementById('logo'); let myWeakmap = new WeakMap(); myWeakmap.set(myElement, {timesClicked: 0}); myElement.addEventListener('click', function() { let logoData = myWeakmap.get(myElement); logoData.timesClicked++; }, false);//上面代码中,myElement是一个 DOM 节点,每当发生click事件,就更新一下状态。我们将这个状态作为键值放在 WeakMap 里,对应的键名就是myElement。一旦这个 DOM 节点删除,该状态就会自动消失,不存在内存泄漏风险。 const _counter = new WeakMap(); const _action = new WeakMap(); class Countdown { constructor(counter, action) { _counter.set(this, counter); _action.set(this, action); } dec() { let counter = _counter.get(this); if (counter < 1) return; counter--; _counter.set(this, counter); if (counter === 0) { _action.get(this)(); } } } const c = new Countdown(2, () => console.log('DONE')); c.dec() c.dec() // DONE 上面代码中,Countdown类的两个内部属性_counter和_action,是实例的弱引用,所以如果删除实例,它们也就随之消失,不会造成内存泄漏。