JavaScript集合对象遍历方法总结

来源:互联网 发布:资源商城源码 编辑:程序博客网 时间:2024/06/10 02:02

在JavaScript中能表示集合的数据结构是对象,如数组、普通对象和ES2015中新增的Set和Map等。当然不同对象的表现形式和功能不一样。如对于集合{1,2,3,4,5,1},数组的表现形式可以为[1,2,3,4,5,1],普通对象的表现形式可以是{"1":2,"2":1,"3":1,"4":1,"5":1},Set的表现形式是{1,2,3,4,5},Map的表现形式为{"1" => 2, "2" => 1, "3" => 1, "4" => 1, "5" => 1}。当然set的主要作用是去重,map的主要作用是统计。新增的数据结构和新增的方法也使得集合的遍历变得很容易。针对这样的对象,我们经常使用的操作就是遍历,本文将总结集合对象遍历的几种方式。

1. 数组的遍历

首先来看一下针对数组的遍历方法,如下:

var arr=[1,2,3,4,5,1];//1.简单的for循环for(var i=0,l=arr.length;i<l;i++){    console.log(`${i}:${arr[i]}`);}//2.forEach循环arr.forEach((item,index)=>console.log(`${item}:${arr[index]}`));//3.for...in循环for(var i in arr){    console.log(`${i}:${arr[i]}`)}//4.for...of循环 //只输出值for(var i of arr){    console.log(`${i}`)} //输出索引和值for(var [key,value] of arr.entries()){    console.log(`${key}:${value}`)}//outputs:0:11:22:33:44:55:1

其实对数组来说,最有效率的方式就是第一种简单for循环了。另外几种虽然可以,但是效率就大打折扣了。for…of循环是ES6新增的方法,用在这里有点大材小用了,不过在这里主要说明的是ES6的数组、Set和Map都拥有entries()方法,它返回一个遍历器对象,用来遍历[键名, 键值]组成的数组。这点可以参见阮一峰老师的《ECMAScript 6 入门》,引用如下:

有些数据结构是在现有数据结构的基础上,计算生成的。比如,ES6的数组、Set、Map 都部署了以下三个方法,调用后都返回遍历器对象。

  • entries() 返回一个遍历器对象,用来遍历[键名, 键值]组成的数组。对于数组,键名就是索引值;对于 Set,键名与键值相同。Map 结构的 Iterator 接口,默认就是调用entries方法。
  • keys() 返回一个遍历器对象,用来遍历所有的键名。
  • values() 返回一个遍历器对象,用来遍历所有的键值。

2. 对象的遍历

var obj={"1":2,"2":1,"3":1,"4":1,"5":1};//1. for...in循环for(var i in obj){    console.log(`${i}:${obj[i]}`)}//2. 利用Object.keys()+forEachObject.keys(obj).forEach(i=>console.log(`${i}:${obj[i]}`));//outputs:1:22:13:14:15:1

对象的遍历可以通过for…in循环得到。如果只想得到对象的属性值,可以用Object提供的静态方法Object.keys()得到。不过这里要说一下for…in循环。for…in循环会返回所有能够通过对象访问的、可枚举(enumerated)的属性,其中既包括存在于实例中的属性,也包括存在与原型中的属性。它准确来说是用来找属性的方法,用在集合遍历(属性值的数据类型通常一样)显得有些大材小用了。不过因为这是一个很强大的方法,强大也意味着容易让人犯错。比如你想造一个类数组对象(对象中有length属性)写了如下语句obj.length=5。当你再用for…in循环是就会郁闷的发现输出多了一个length:5。或许你也许会问数组也是对象,为什么for…in循环的结果没有length呢?

我来解释一下,先来看一下上面的黑体字,返回可枚举的属性。这意味着只有可枚举的属性才会暴露出来。那怎么看是不是可枚举呢?要查看对象属性的特性,可以用Object.getOwnPropertyDescriptor()。比如我们查看length属性在数组中是否可枚举,可使用如下代码:Object.getOwnPropertyDescriptor(arr,'length')。它在Chrome中的返回为Object {value: 6, writable: true, enumerable: false, configurable: false}。里面的enumerable值为false,意味着不可枚举。configurable为false说明不可删除。所以length属性在数组中默认是不可枚举,也不能用delete删除的。那有没有办法修改哪?要修改属性默认的特性,可以使用Object.defineProperty()。但数组中的length属性默认是不可修改的。说了这么多,我们给对象新添加的length属性返回什么呢?试一下,就会发现它返回Object {value: 4, writable: true, enumerable: true, configurable: true}。因为configurable值为true,所以可被枚举到。不过针对我们自己创建的对象,可以修改属性类型。使用Object.defineProperty(obj,'length',{enumerable: false})设置可枚举为false之后,发现再利用for…in循环就不会再遍历到length属性了。这里的属性类型的具体内容可以参见JavaScript高级程序设计6.1节。

另外针对Object.keys()要注意,若传入的参数为非对象,ES6工作方式同ES5不一样。ES5会报错,ES6会将其强制转换为对象,若转换不成功报错。

Object.keys('abcd')//es5:TypeError...//es6:["0", "1", "2", "3"]

3. Set的遍历

//Set作为构造函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数var set=new Set([1,2,3,4,5,1])//Set {1, 2, 3, 4, 5}//也可以使用add方法添加成员var set=new Set();set.add(1);set.add(2);set.add(3);set.add(4);set.add(5);set.add(1);//Set {1, 2, 3, 4, 5}//1.for...of遍历值for(var i of set){    console.log(`${i}`)}for(var [key,value] of set.entries()){    console.log(`${key}:${value}`)}//2.forEach遍历键值对set.forEach((value, key) => console.log(`${key}:${value}`) )

Set和Map的加入可谓是呼声已久。因为很长时间我们都只能依靠普通对象来实现Set和Map的功能。所以如果JS原生支持的话,那再好不过了。Set的遍历可以用for…of或forEach来实现。如果使用set.entries(),返回的数组键名和键值相等。set.keys()set.values()也会得到相同的集合。
得益于ES6的方法,数组和Set可以迸发出很强大能量。比如数组转Set,直接向Set的构造函数中传递数组就会得到一个去重的Set;Set转数组,同样很简单,直接使用Array.from或扩展运算符就Ok了。(扩展运算符可以将任何实现了 Iterator 接口的对象转化为数组,Set实现了 Iterator 接口。)二者简直天作之合。比如有些时候需要对数组去重而后排序,二者合作简直不能太美。

var arr=[5,3,1,2,3,4,5,1];var set=new Set(arr);//Set {5,3,1,2,4}arr=Array.from(set);//也可以使用扩展运算符arr=[...set];arr.sort((a,b)=>a-b);//output:[1, 2, 3, 4, 5]

4. Map的遍历

//Map作为构造函数可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组var map=new Map([["小明",2],["小红",2]])//Map {"小明" => 2, "小红" => 2}//也可以使用set设置键值对var map=new Map();map.set(1,2);map.set(2,1);map.set(3,1);map.set(4,1);map.set(5,1);//Map {1 => 2, 2 => 1, 3 => 1, 4 => 1, 5 => 1}//1.for...of遍历    //遍历键值对for(var [key,value] of map){    console.log(`${key}:${value}`)}    //遍历值for(var value of map.values()){    console.log(`${value}`)}    //遍历键for(var key of map.keys()){    console.log(`${key}`)}//2.forEach遍历键值对map.forEach((value, key) => console.log(`${key}:${value}`) )

for…of做map的遍历时,默认方式是返回键值对,同使用map.entries()相同,不过可以通过使用map.keys()map.values()只返回键名或键值。Map和Set都提供forEach接口,和数组使用方式相同。
Map和数组的转换同样简单:

//数组转mapvar arr=[["小明",2],["小红",2]];var map=new Map(arr)//{"小明" => 2, "小红" => 2}//map转数组arr=[...map];//[Array[2], Array[2]]

好了,这就是本文的内容。不过,另外说一下只有提供Iterator 接口的数据结构才可以使用for…of循环,才可以使用扩展操作符转化为数组。原生具备该接口的有:

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的 arguments 对象
  • NodeList 对象

对于Set和Map更多的用法请自行查看文末的参考资料。

如有错误,请不吝指正;如有问题,请写下评论。

参考资料:
1. ECMAScript 6 入门