你的位置:首页 > Java教程

[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) //输出:1console.log(setIner.next().value) //输出:2console.log(setIner.next().value) //输出:3console.log(setIner.next().value) //输出:4</script>

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

  整个DEMO:

var mySet = new Set();//往mySet里面添加数据, 1 , 5mySet.add(1);mySet.add(5);mySet.add("some text");//添加对象var o = {a: 1, b: 2};mySet.add(o);mySet.has(1); // 返回:truemySet.has(3); // 返回:falsemySet.has(5);       // 返回:truemySet.has(Math.sqrt(25)); // 返回:truemySet.has("Some Text".toLowerCase()); // t返回:ruemySet.has(o); // 返回:truemySet.size; // 4mySet.delete(5); // 从mySet里面删除5mySet.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