视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
ES6新特性-JavaScript中Set和WeakSet类型的数据结构的代码详细介绍
2020-11-27 20:24:23 责编:小采
文档
ES6提供了新的数据结构Set,Set对象不是数组, 可以用来保存对象或者基本类型, 所有保存的值都是唯一的, 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>

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()方法,手动去获取每一个值;

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里面的该节点如果不存在别的引用的话, 一段时间内会被内存回收;

    下载本文
    显示全文
    专题