zoukankan      html  css  js  c++  java
  • ES6新特性:Javascript中Set和WeakSet类型的数据结构

      ES6提供了新的数据结构SetSet对象不是数组, 可以用来保存对象或者基本类型, 所有保存的值都是唯一的, chrome浏览器>38和FF>13,以及nodeJS,对Set支持良好, 以下的一些代码,都可以拷贝到控制台直接运行哦;

      创建Set实例的基本方法为:

    let set = new Set(); //或者 new Set(null);
    console.log(set);

      或者这样:

    let set = new Set([1,2,3,4,4,4,4,4]);
    console.log( Array.from(set) ); //输出:[ 1, 2, 3, 4 ]

      可以看到,以上重复的4,在set里面只保存了一个, 所以Set对象可以用来给数组去重;

      Set也能用来保存NaN和undefined, 如果有重复的NaN, Set会认为就一个NaN(实际上NaN!=NaN);

      实例Set以后的对象拥有这些属性和方法

        属性
        Set.prototype
        Set.prototype.size
        方法
        Set.prototype.add()
        Set.prototype.clear()
        Set.prototype.delete()
        Set.prototype.entries()
        Set.prototype.forEach()
        Set.prototype.has()
        Set.prototype.values()
        Set.prototype[@@iterator]()

      Set这种类型的数据结构其实我们可以直接用数组模拟出来, 虽然不能和原生的比, 只能模拟以上列表的一些方法属性( 还有一些功能无法实现的 , Set实例的[Symbol.species]指向自己, 但是chrome中没有[Symbol.species]这个玩意儿.... )

      使用数组模拟一个Set构造器:

    <html>
    <head>
        <meta charset="utf-8">
    </head>
    <body>
    <script>
        "use strict";
        class Set {
            //对_set进行去重;
            static refresh () {
                let _this = this;
                let __set = []
                this._set.forEach(function(obj) {
                    if( __set.indexOf(obj) === -1 && obj!=undefined) {
                        __set.push(obj);
                    }
                });
                _this._set =__set;
                this.size = _this._set.length;
            }
            constructor(arg) {
                this.size = 0;
                this[Symbol.species] = this;
                this._set = Array.isArray(arg)&&arg||[];
                Set.refresh.call(this)
            }
            add (obj) {
                this._set.push(obj);
                Set.refresh.call(this)
                return this;
            }
            clear () {
                this._set.length = 0;
                return this;
            }
            delete (obj) {
                if( this._set.indexOf(obj)!=-1 ) {
                    this._set[this._set.indexOf(obj)] = undefined;
                };
                Set.refresh.call(this);
                return this;
            }
            /**
             * @desc
             * @return Entries [[],[],[],[]]
             * */
                    entries () {
                let result = [];
                this.forEach(function(key, value) {
                    result.push([key,value]);
                });
                return result;
            }
            has () {
                if( this._set.indexOf(obj)!=-1 ) return true;
            }
            keys () {
                return this[Symbol.iterator]();
            }
            values () {
                return this[Symbol.iterator]();
            }
            //直接使用数组的forEach方便啊;
            forEach (fn, context) {
                let _this = this;
                this._set.forEach((value) => fn.call(context||value, value, value, _this) );
            }
            //必须支持生成器的写法;
        *[Symbol.iterator] (){
            let index = 0;
            let val = undefined;
            while(index<this.size) {
            val = this._set[index];
            yield  val;
            index++;
        }
        }
        }
        var set = new Set([0,0]);
        //对Set进行基本的操作;
        set.add(1).add(2).add(3).add({1:1})
        set.delete(1);
        set.add(1);
        //使用Set的forEach方法;
        set.forEach(function(key,value,s){console.log(key,value,s,"this")},{this:"this"})
        //检测生成器是否正常运行;
        for(let s of set) {
            console.log(s)
        }
        //因为这个对象有Symbol.iterator, 所以使用扩展符也是好使的;
        console.log([...set]);
    </script>
    </body>
    </html>
    View Code

      Set实例的属性:

      size属性:size是指这个Set的长度,和数组的length效果一样的"
      constructor属性: 这个属性指向Set构造函数 ,这个代码即可实现 (new Set).constructor === Set //输出:true

      Set实例的方法:

      add方法,往set添加数据;

    <script>
        Array.from((new Set([1,2])).add(3)); // 输出:[1, 2, 3]
    </script>

      

      clear方法,把set里面的数据清空;

    let set = (new Set([1,2,3,4]));
    set.clear();
    Array.from(set);

      delete方法,删除set里面的指定数据:

    let set = (new Set([1,2,3,4]));
    set.delete(1);
    Array.from(set); //输出:[2, 3, 4]

      entries方法:

    let set = (new Set([1,2,3,4]));
    Array.from(set.entries());

      forEach方法:set的forEach有两个参数, 第一个参数为一个函数,第二个参数是非必须的,如果传了第二个参数, 那么该函数的上下文this就是我们传的第二个参数:

    <script>
    let set = (new Set([1,2,3,4]));
    set.forEach(function() {
        console.log(arguments);
        console.log(this)
    },"1111");
    </script>

      输出:

      

      has方法, has是判断这个set是否有指定的值, 返回false或者true;

    <script>
    let set = (new Set([1,2,3,4]));
    console.log(set.has(1)) //输出:true;
    console.log(set.has(5)) //输出:false
    </script>

      keys方法和values()方法, 这两个方法都是返回一个迭代器;

    <script>
    let set = new Set([1,2,3,4]);
    console.log(set.keys());
    console.log(set.values());
    
    var keys = set.keys();
    for(let key of keys) {
        console.log(key);
    };
    </script>

      @@iterator()方法, @iterator方法是set默认的迭代器;

    <script>
    let set = new Set([1,2,3,4]);
    let setIner = set[Symbol.iterator]();
    console.log(setIner.next().value) //输出:1
    console.log(setIner.next().value) //输出:2
    console.log(setIner.next().value) //输出:3
    console.log(setIner.next().value) //输出:4
    </script>

      实际上我们可以重写set[Symbol.iterator],但是不会对set的keys和values方法产生影响;

      整个DEMO:

    var mySet = new Set();
    //往mySet里面添加数据, 1 , 5
    mySet.add(1);
    mySet.add(5);
    mySet.add("some text");
    //添加对象
    var o = {a: 1, b: 2};
    mySet.add(o);
    
    mySet.has(1); // 返回:true
    mySet.has(3); // 返回:false
    mySet.has(5);              // 返回:true
    mySet.has(Math.sqrt(25));  // 返回:true
    mySet.has("Some Text".toLowerCase()); // t返回:rue
    mySet.has(o); // 返回:true
    
    mySet.size; // 4
    
    mySet.delete(5); // 从mySet里面删除5
    mySet.has(5);    // 输出:false, 5 已经被删除了
    
    mySet.size; // 现在的长度为:3
    
    // 通过 for...or循环获取数据;
    // 输出: 1, "some text"
    for (let item of mySet) console.log(item);
    
    // 输出: 1, "some text"
    for (let item of mySet.keys()) console.log(item);
    
    // 输出: 1, "some text"
    for (let item of mySet.values()) console.log(item);
    
    // 输出: 1, "some text", 对于Set来说:key和value是一样的
    for (let [key, value] of mySet.entries()) console.log(key);
    
    // 把迭代器转化为数组的第一种方式;
    var myArr = [v for (v of mySet)]; // [1, "some text"]
    // 把迭代器转化为数组的第二种方式;
    var myArr = Array.from(mySet); // [1, "some text"]
    // 也可以用next()方法,手动去获取每一个值;
    View Code

      Set的实际用处:

      利用set可以方便的进行交集和并集:

      求并集, 我们可以给两个方案或者更多:

    var union = (setA, setB) => {
        //[...setA]这种方式目前只有babel才支持
        return new Seet([...setA,...setB]);
    };
    var union = (setA, setB) => {
        return new Set(Array.from(setA).concat(Array.from(setB)));
    }

      这种获取交集的方式,和数组求交集差不多;

    var intersect = (set1, set2) => {
        //return [x for (x of set1) if (set2.has(x))]; 这种写法完全不行嘛....
        var resultSet = new Set();
        for(let set of set1) {
            if(set2.has(set)) {
                resultSet.add(set);
            };
        };
        return resultSet;
    };

      以下这种代码更短,太酷了啊, 这个方法来自:http://es6.ruanyifeng.com/#docs/set-map;

    var intersect = (set1, set2) => {
        return new Set([...set1].filter(x => set2.has(x)));
    }
    console.log(intersect(new Set([1,2,3,4]), new Set([2,3,4,5]))); //输出:Set {2,3,4}

      弱引用的WeakSet

      WeakSet对象是一些对象值的集合, 并且其中的每个对象值都只能出现一次,WeakSet只能存对象类型的元素,比如:Object, Array, Function 等等;有了弱引用的WeakSet, 就不用担心内存泄漏了,如果别的对象不引用该对象, 这个对象会被垃圾回收机制自动回收;

    <script>
        console.log(new WeakSet([{},[],()=>({1:1})]));
    </script>

      WeakSet对象的方法只有三个,而且WeakSet对象没有size属性;
        weakSet.add();
        weakSet.delete();
        weakSet.has();

      如果对象不存在引用, 那么WeakSet对象会把没有引用的对象占用的内存回收, 下面这个demo,你可以跑一下, 然后过一会儿(我的chrome浏览器10S就看到效果了)再看控制台:

    <script>
    var ws = new WeakSet()
    var obj = {}; ws.add(obj);
    ws.add([])
    setInterval(()=>{
        console.log(ws);
    },1000)
    </script>

      weakSet可以用来保存DOM节点, 当节点被删除, weakSet里面的该节点如果不存在别的引用的话, 一段时间内会被内存回收;

       参考:

      MDN:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set

      ruanyifeng:http://es6.ruanyifeng.com/#docs/set-map

    作者: NONO
    出处:http://www.cnblogs.com/diligenceday/
    QQ:287101329
    微信:18101055830 

  • 相关阅读:
    Eclipse常用快捷键
    Kali Linux安装Google中文输入法(只需5步)
    Kali Linux 更新源 操作完整版教程
    Oracle存储过程的异常处理
    Eclipse调试DEBUG时快速查看某个变量的值的快捷键、快速跳转到某行的快捷键
    oracle listagg和wm_concat函数
    ORACLE分页查询SQL语法——最高效的分页
    Mock拦截请求URL返回模板数据
    前端安全之XSS攻击及防御
    Sublime Text3注册码,亲测可用
  • 原文地址:https://www.cnblogs.com/diligenceday/p/5479076.html
Copyright © 2011-2022 走看看