首页 > 代码库 > ES6-Set and Map

ES6-Set and Map

依赖文件地址 :https://github.com/chanceLe/ES6-Basic-Syntax/tree/master/js

  1 <!DOCTYPE html>
  2 <html>
  3     <head>
  4         <meta charset="UTF-8">
  5         <title>[es6]-12-Set和Map结构</title>
  6         <script src="./js/browser.js"></script>
  7         <script type="text/babel">
  8             /*
  9              * Set 
 10              * Es6提供了新的数据结构Set,类似数组,成员都是唯一的,不重复。
 11              * 本身是一个构造函数,用来生成Set数据结构。
 12              */
 13             
 14             var s = new Set();
 15             [2,3,5,4,5,2,2].map(x=>s.add(x));
 16             console.log(s);  //2,3,5,4   不会重复添加
 17             
 18             //Set函数可以接收一个数组(或类似数组的对象)作为参数,用来初始化。
 19             var set = new Set([1,2,3,4]);
 20             console.log([...set]);  //[1,2,3,4]
 21             
 22             var set2 = new Set([1,2,3,4,5,5,5,5,5,5]);
 23             console.log(set2.size);  //5  没有重复值
 24             
 25             var set3 = new Set([...document.querySelectorAll("div")]);
 26             console.log(set3.size);   //3
 27             
 28             //上面代码中也展示了一种数组去重方法    [...new Set(array)]
 29             console.log([...new Set([1,2,3,2,3,2,3])]);   //[1,2,3]
 30             
 31             /*
 32              * 向Set加入值的时候,不会发生类型转换,所以5 和“5” 是不一样的。
 33              * Set内部的比较算法是 same-value-equality。类似“===”主要的区别是NaN等于自身,三等号认为NaN不等于NaN。
 34              * 在set内部,NaN等于NaN.但两个对象总是不相等。
 35              */
 36             let set4 = new Set();
 37             set4.add({});  
 38             console.log(set4.size);   //1
 39             set4.add({});
 40             console.log(set4.size);   //2  表示两个对象在Set内是不相等的。
 41             set4.add(NaN);
 42             console.log(set4.size);    //3
 43             set4.add(NaN);
 44             console.log(set4.size);   //3   都是3表明NaN在Set内部是相等的。
 45             
 46             /*
 47              * Set实例的属性和方法
 48              * 属性:   Set.prototype.constructor:  构造函数,默认就是Set。
 49              *            Set.prototype.size  :  返回Set成员数。
 50              * 
 51              * 方法:分为两类,操作方法和遍历方法
 52              *      四个操作:
 53              *       add(value)  添加某个值,返回Set结构本身。
 54              *          delete(value) 删除某个值,返回布尔值,表示删除是否成功。
 55              *          has(value) 返回一个布尔值,表示该值是否为Set的成员。
 56              *          clear() 清除所有成员,没有返回值。
 57              */
 58             
 59             //Array.from()可以把Set结构转为数组,所以又有了一种去重的方法
 60             function dedupe(arr){
 61                 return Array.from(new Set(arr));
 62             }
 63             console.log(dedupe([1,2,3,3,3,]));  //[1,2,3]
 64             
 65             /*
 66              * Set结构的实例有四个遍历方法,可用于遍历成员。
 67              *   keys()  返回键名的遍历器
 68              *   values() 返回键值的遍历器
 69              *   entries() 返回键值对的遍历器
 70              *   forEach()  使用回调函数遍历每个成员
 71              * 
 72              * 需要特别指出的是 Set的遍历顺序就是插入顺序。
 73              * Set结构中,键名跟键值是同一个,所以keys 跟values的结果一样。
 74              */
 75             let set5 = new Set(["green","blue","red"]);
 76             for(let key of set5.keys()){
 77                 console.log(key,"key")
 78             }
 79             for(let value of set5.values()){
 80                 console.log(value,"value")
 81             }
 82             for(let entry of set5.entries()){
 83                 console.log(entry,"entry")
 84             }
 85 
 86              // Set结构默认可遍历,他的默认遍历器就是values方法
 87             console.log(Set.prototype[Symbol.iterator] == Set.prototype.values);  //true
 88             //这意味着可以省略values方法 直接用for of 循环
 89             
 90             for(let value of set5){
 91                 console.log(value,"value")   //默认是values方法
 92             }
 93             
 94             //forEach   参数是一个处理函数。参数函数的参数依次是键值,键名,Set本身,forEach方法本身
 95             //还可以有第二个参数,表示绑定的this对象。
 96             
 97             //扩展运算符内部使用的是for...of,所以同样可以用于集合。
 98             console.log([...set5]);  //["green","blue","red"]
 99             
100             //数组的map和filter方法也可以用于Set
101             let set6 = new Set([1,2,3]);
102             set6 = new Set([...set6].map(x=>x*2));
103             console.log(set6);  //{2,4,6}
104             
105             let set7 = new Set([1,2,3,4,5]);
106             set7 = new Set([...set7].filter(x=>(x % 2)==0));
107             console.log(set7);  //{2,4}
108             
109             //使用Set可以很容易实现并集交集差集
110             
111             let a = new Set([1,2,3]);
112             let b = new Set([4,3,2]);
113             
114             //并集
115             let union = new Set([...a,...b]);
116             console.log(union);  //{1,2,3,4}
117             
118             //交集
119             let intersect = new Set([...a].filter(x=>b.has(x)));
120             console.log(intersect)  //{2,3}
121             
122             //差集
123             let diff = new Set([...a].filter(x=>!b.has(x)));
124             console.log(diff);  //{1}
125             /*
126              * 如果想在操作中改变原来的Set结构,目前没有直接的方法,但有两个变通方法,
127              * 一种是利用原Set映射出一个新的结构,然后赋值给原来的Set结构;
128              * 一种是利用Array.from方法
129              */
130             //方法一
131             let set8 = new Set([1,2,3]);
132             set8 = new Set([...set8].map(x=>x*2));
133             console.log(set8);   //{2,4,6}
134             
135             //方法二
136             let set9 = new Set([1,2,3]);
137             set9 = new Set(Array.from(set,val=>val*2));
138             console.log(set9);   //{2,4,6}
139             
140             /*
141              * WeakSet
142              * 结构与Set类似,但只能存放对象,不能存放其他类型的成员。
143              * WeakSet中的对象都是弱引用,即不在垃圾回收机制的考虑范围内。
144              * 这意味着无法引用WeakSet的成员,因此WeakSet不可遍历。
145              * 不能获取size和forEach属性。
146              * 一个用处是,储存DOM节点,而不担心这些节点从文档中移除时,会引发内存泄漏下面是另一个例子:
147              */
148             const foos = new WeakSet();
149             class Foo{
150                 constructor(){
151                     foos.add(this);
152                 }
153                 method(){
154                     if(!foos.has(this)){
155                         throw new TypeError("Foo.prototype.method 只能在Foo的实例上调用")
156                     }
157                 }
158             }
159             //上面代码保证了Foo的实例方法,只能在Foo的实例上调用。这里使用WeakSet的好处是foos对实例的引用,
160             //不会被计入内存回收机制。所以删除实例的时候不用考虑foos,也不会出现内存泄漏。
161             
162             
163             
164             /*
165              * Map
166              * Map结构的目的和基本用法
167              * js的对象本质上是键值对的集合(hash结构),但是传统上只能用字符串当做键。这给使用带来了很大限制。
168              * Es6提供了Map数据结构,类似对象,也是键值对的结合,但键的范围不限于字符串,各种类型的值都可以当做键。
169              * 也就是说Object结构提供了“字符串-值”的对应。Map结构提供了“值-值”的对应。是一种更完善的哈希结构实现。
170              */
171             var m = new Map();
172             var o = {p:"hello world!"}
173             
174             m.set(o,content);
175             console.log(m.get(o));  //content
176             console.log(m.has(o));   //true
177             console.log(m.delete(o));  //true
178             console.log(m.has(o));  //false
179             /*
180              * 上面代码使用set方法,将对象o当做m的一个键,然后使用get方法读取这个键,接着用delete方法删除了这个键。
181              * 作为构造函数,Map也可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组。
182              */
183             var m2 = new Map([
184                 ["name","张丹"],
185                 ["age",20]
186             ])
187             console.log(m2.size);  //2
188             console.log(m2.has("name"));  //true 
189             console.log(m2.get("name"));   //张丹
190             
191             //下面的例子中,字符串true和布尔true是两个不同的键
192             var m3 = new Map([
193                 [true,"1"],
194                 ["true","string"]
195             ]);
196             console.log(m3.get("true"));  //string
197             console.log(m3.get(true));   //1
198             
199             //如果对同一个键多次赋值,后面的值将覆盖前面的值
200             m3.set(true,"number");
201             console.log(m3.get(true));  //number
202             
203             // 如果读取一个未知的键,则返回undefined。注意,只有对同一个对象的引用,Map
204             // 才将其视为同一个键,这一点要非常小心。
205             
206             var m4 = new Map();
207             m4.set(["s"],555);
208             console.log(m4.get(["s"]));  //undefined   因为这俩不是同一个对象的引用。
209             
210             //同理,同样的值的两个实例,在Map结构中被视为两个键。
211              
212             var m5 = new Map();
213             var k1 = ["a"];   
214             var k2 = ["a"];   //k1其实跟k2的内存地址不一样。
215             m5.set(k1,"1111")
216             m5.set(k2,"2222")
217             console.log(m5.size);   //2  表明这是两个键。
218             
219             //由上可知,Map实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。这就解决了同名
220             //属性碰撞的问题。我们扩展别人库的时候,如果使用对象作为键名,就不用担心自己的属性与原作者
221             //重名。
222             /*
223              * 如果Map的键是一个简单类型的值(数字,字符串,布尔值),则只要两个严格相等,Map将其视为一个键。
224              * 包括+0和-0。另外,虽然NaN不等于自身,但Map将其视为同一个键。
225              */
226             
227             var m6 = new Map();
228             m6.set(NaN,"123");
229             console.log(m6.get(NaN));  //123
230             m6.set(-0,"456");
231             console.log(m6.get(0));  //456
232             
233             /*
234              * 实例的属性和操作方法
235              *   属性:  size  返回大小
236              *   方法:  set 添加  get 获取,找不到返回undefined    has 判断是否存在,返回布尔
237              *             delete删除,返回布尔值  clear清空。
238              * 
239              *   遍历方法跟Set一样,keys,values,entries,forEach
240              *   Mapc的默认遍历器是entries,是键值对。
241              *    
242              *   Map转为数结构,比较快速的是用...  扩展运算符。
243              * 
244              * Map还有一个forEach方法,与数组的forEach方法类似,也可以实现遍历。
245              * 这里的forEach接受第二个参数,绑定this。
246              */
247             var reporter = {
248                 report:function(key,value){
249                     console.log("key:%s,value :%s",key,value);
250                 }
251             }
252             console.log(reporter.report);
253             var m7 = new Map([
254                 ["name","zhangdan"],
255                 ["age",25]
256             ])
257             m7.forEach(function(value,key,map){
258                 this.report(key,value);
259             },reporter)
260             
261             //上面的handler函数不能用箭头函数,原因是箭头函数的this 是死的。
262             
263             /*
264               Map与其他结构互转
265              */
266             
267             //1.map转为数组 ,...扩展运算符。
268             var m8 = new Map().set("add","+").set("plus","-");
269             console.log([...m8]);
270             
271             //2.数组转为Map,将数组传入构造函数。
272             var m9 =new Map([["name","张丹"],["age",26]]);
273             
274             //3.如果map的所有键都是字符串,那就可以转成对象。
275             var strMaptoObj = (strMap) =>{
276                 let obj = Object.create(null);
277                 for(let [k,v] of strMap){
278                     obj[k] =v;
279                 }
280                 return obj;
281             }
282             console.log( typeof(strMaptoObj(m9)));  //object
283             
284             //4.对象转为Map
285             var objToStrMap = (obj)=>{
286                 let strMap = new Map();
287                 for(let k of Object.keys(obj)){
288                     strMap.set(k,obj[k]);
289                 }
290                 return strMap;
291             }
292             console.log(objToStrMap({yes:true,no:false}));   //Map
293             
294             //5.Map转Json,分为两种情况。
295             //一种是 Map的键名都是字符串,这时可以转为对象JSON。
296             var strMapToJson = (strMap) =>JSON.stringify(strMaptoObj(strMap));
297             let m10 = new Map().set("yes",true).set("no",false);
298             console.log(strMapToJson(m10));  //{"yes":true,"no":false}
299             
300             //另一种是Map的键名含有非字符串,这时可以转为数组JSON。
301             var mapToArrayJson = (map) => JSON.stringify([...map]);
302             let m11 = new Map().set(true,"1").set({foo:3},["abc"]);
303             console.log(mapToArrayJson(m11));   //[[true,"1"],[{"foo":3},["abc"]]]  数组json
304             
305             //6.JOSN转Map
306             //正常情况下,所有键名都是字符串
307             var jsonToStrMap = (jsonStr) => objToStrMap(JSON.parse(jsonStr));
308             console.log(jsonToStrMap({"yes":true,"no":false}));  //Map {"yes" => true, "no" => false}
309             
310             //但有一个特殊情况,整个JSON是一个数组,且每个数组成员本身,又是一个有两个成员的数组,这时,可以一一对应的转为Map。
311             //这往往是数组转JSON的逆操作。
312             var jsonToMap = (jsonStr) => new Map(JSON.parse(jsonStr));
313             console.log(jsonToMap([["name","zhangdan"],["age",25]]));
314                                 //Map {"name" => "zhangdan", "age" => 25}
315                                 
316                                 
317             /*
318              * WeakMap  
319              * 跟WeakSet类似,也是只接受成员为对象(null除外),而且键名所指向的对象,不计入垃圾回收机制。
320              * WeakMap的键名可能会被自动回收,当对象被回收后,WeakMap自动移除对应的键值对。
321              * 典型的应用是,一个DOM元素的WeakMap结构,当某个DOM元素被清除,其所对应的WeakMap记录自动被移除。
322              * 基本上,WeakMap的专用场合就是,它的键所对应的对象,可能会在未来消失。
323              * WeakMap有助于防止内存泄漏。
324              * 
325              * WeakMap的另一个用处就是部署私有属性。
326              */
327             {
328                 let _counter = new WeakMap();
329                 let _action = new WeakMap();
330                 
331                 class Countdown{
332                     constructor(counter,action){
333                         _counter.set(this,counter);
334                         _action.set(this,action);
335                     }
336                     dec(){
337                         let counter = _counter.get(this);
338                         if(counter<1){
339                             return 
340                         }
341                         counter--;
342                         _counter.set(this,counter);
343                         if(counter === 0){
344                             _action.get(this)();
345                         }
346                     }
347                 }
348                 
349                 let c = new Countdown(2, ()=>{console.log("Done")});
350                 c.dec();
351                 c.dec();
352                 
353                 //Done
354             }
355         </script>
356     </head>
357     <body>
358         <div></div>
359         <div></div>
360         <div></div>
361     </body>
362 </html>

 

ES6-Set and Map