• 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>
  • 相关阅读:
    加法的位运算实现
    C++装饰器模式
    字符串类型的相互转换
    手算CRC及其实现
    Linux下搭建C/C++编程环境
    Deepin Linux 实体机安装
    Atom + Texlive 配置 Latex 环境
    有关字符串的算法(KMP,Manacher,BM)陆续补充
    Linux 下大文件分割与合并
    Ubuntu /目录满,发现是docker image 太多解决办法
  • 原文地址:https://www.cnblogs.com/chengyunshen/p/7191663.html
Copyright © 2020-2023  润新知