js全部循环遍历方法

150 阅读5分钟

js的遍历

前要

本文也是作者在开leetcode专栏前的前置知识复习巩固 将分类的学习遍历方法。

Iterator(遍历器)

Iterator是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。

Iterator 的作用有三个:一是为各种数据结构(ObjiectArraySetMap...),提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for...offor...in)循环,Iterator 接口主要供for...of(for...in)使用。

具备 Iterator 接口的数据有这几个:

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

也就是说对象不能通过Iterator遍历哦,这就是for...of为什么不能遍历对象

简单的实现一下Iterator,并且实现遍历

//Iterator迭代器
function _Iterator(array) {
  let index = 0;//当前下标
  return {
    next: () => {
    //如果遍历结束,则返回{done:true}
      if (index < arr.length) {
        return { value: array[index++] };
      } else return { done: true };
    },
  };
}

//假设这是for...of的函数形式
function forOf(arr, func) {
  const It = _Iterator(arr);
  while (1) {
    const item = It.next();
    if (item.value) {
      func(item.value);
    } else break;
  }
}

const arr = [1, 2, 3];
forOf(arr, (value) => {
    //在这里用户对value做一些操作,for...of直接获取到value。
  console.log(value);
  // 1 2 3
});

我们可以看到Iteraotr的功能就是帮助实现迭代,而Iterator还有其它使用场景,如下

  • 解构赋值时使用
  • ...扩展运算符也是使用了Iterator
  • yield* 后跟可遍历结构
  • Promise.all,Promise.race

由于每一种类型的数据/对象都有很多种方法遍历,我们这里将每一种方法的常用方式说一就好了。

for

const list = [1, 2, 3, 4, 5];
let obj={
}

// 用let不能用var不用多说了吧,抛弃var!!
for (let i = 0; i < list.length; i++) {
  console.log(list[i]); // 1 2 3 4 5
}

for朴素但好用

for...of

const list = [1, 2, 3, 4, 5];

//const 因为value不能被修改
for (const item of list) {
  console.log(item);  //1,2,3,4,5
}

它一般用来遍历数

for...in

由于对象不具有Iterator接口,所以for...in能够对对象遍历,也会遍历继承的属性,并返回键值。

const obj = {
  a: 1,
  b: 2,
  c: 3,
};
Object.prototype.d = 4;
for (let i in obj) {
  console.log(i); // a b c d
  console.log(obj[i]); //1 2 3 4
}

但是不建议在ts中使用for...in,我的建议是写ts时直接扔掉for...in

forEach

forEach一般也是用于遍历数组,只不过循环体内的获取变量更多

const arr = [1, 2, 3];

const str = "string";

arr.forEach((item, index, arr) => {
//item,index,arr很形象
  console.log(index); // 0 1 2
  console.log(item); // 1 2 3
});

tip: 如果想要改变数组的值,就使用forEach,单纯的遍历就使用for...of

map

map返回一个新数组

const objArr = [
  { name: "11", age: 12 },
  { name: "22", age: 13 },
  { name: "33", age: 14 },
];

const mapArr = objArr.map((value, idnex, arrar) => {
  return value.name; //[ '11', '22', '33' ]
});

every some

every和some都是用于判断数组内的值。

const arr = [1, 2, 3];

const someRes = arr.some((arr) => arr == 3);
// 判断arr是否包含3这个值
console.log(someRes);  //true

const everyRes = arr.every((item, index, arr) => {
  return item === 1;  
  // 是否每个值都等于1
});

console.log(everyRes);  //false

filter

顾名思义,它用来过滤,return中进行判断是否保留,然后返回一个新的数组

const arr = [1, 2, 3];

const newArr = arr.filter((item) => {
  return item === 2;
});
console.log(newArr); // [2]

find findIndex

find用于寻找数组中是否包含满足条件的值,并将其返回,不存在将返回undefined

而findIndex则只是返回的结果不一样,它返回的是index下标,未找到返回-1

const list = [
{ name: 'fir', id: 1 },
{ name: 'sec', id: 2 },
{ name: 'thi', id: 3 },
];
const result = list.find((item) => item.id === 3);
// result: { name: 'thi', id: 3 }

const index = list.findIndex((item) => item.id === 3);
// index: 2
console.log(list[index].name) //thi ;

reduce

reduce,可以称作返回数组每一项经回调函数处理之后的结果

const objArr = [
  { name: "11", age: 12 },
  { name: "22", age: 13 },
  { name: "33", age: 14 },
];

const total = objArr.reduce(
    //参数 上次的值,当前值,当前下标,数组(值指的是后面的number,这里是0)
  (previousValue, currentValue, currentIndex, array) => {
    return previousValue + currentValue.age;
  },
  0
);
console.log(total);   //39

我个人觉得没有使用这个方法的必要,因为它本身用for循环处理也不麻烦,反而是这种无关紧要的方法太多了,容易记混淆。记起来也麻烦,因为它还可以处理对象。确实reduce可以做到很多东西。

数组遍历

其实上面的这些方法除了for...in,都是主要用于遍历数组,那么我们一定会考虑一个问题,用哪个最好?

我的观点是不需要改变原数组就用for,因为它是最快的!

需要改变原数组就用forEach,它不仅快还提供足够的参数

需要一个新数组的话就使用map!!

对象遍历

除了前面说的for...in,遍历对象基本上就是通过Object自带的方法来实现遍历。其实,遍历对象本来就不是重要的需求,我们一般通过索引获取对象的属性的值就基本够了,ts的很多功能也是通过索引来实现的。

数组,String,实际上也是对象不用多说了吧。内存里面开辟了空间,然后通过引用指向这块空间。

Object.keys

这是Object的方法,用于返回指定对象的key的字符数组,但不包括原型中的属性

const objArr = [  { name: "11", age: 12 },  { name: "22", age: 13 },  { name: "33", age: 14 },];

console.log(Object.keys(objArr[1]))  //[ 'name', 'age' ]
console.log(Object.keys(objArr));  //['0','1','2']

一般也就是用于获取对象的key,因为数组的key没有实用性。

Object.values

返回对象自身所有的可枚举的属性值 组成的数组。

const str = "string";
console.log(Object.values(str));  //[ 's', 't', 'r', 'i', 'n', 'g' ]
//分解字符倒是挺方便的
const objArr = [  { name: "11", age: 12 },  { name: "22", age: 13 },  { name: "33", age: 14 },];

console.log(Object.values(objArr));  //和objArr一样

console.log(Object.values(objArr[1]));  //[ '22', 13 ]

虽然说数组操作返回了没有发生改变的数组 但是看下面

const objArr = [
  { name: "11", age: 12 },
  { name: "22", age: 13 },
  { name: "33", age: 14 },
];
const arr2 = Object.values(objArr);
console.log(arr2 === objArr);  //false

嗯...完成了深拷贝。毕竟还是开辟了新的内存空间。

Object.entries

const arr = [1, 2, 3];

const objArr = [  { name: "11", age: 12 },  { name: "22", age: 13 },  { name: "33", age: 14 },];

console.log(Object.entries(arr));  //[ [ '0', 1 ], [ '1', 2 ], [ '2', 3 ] ]
console.log(Object.entries(objArr));
console.log(Object.entries(objArr[1])); //[ [ 'name', '22' ], [ 'age', 13 ] ]
返回一个二维数组,每一个子数组由对象的属性名、属性值组成。(将key和value结合一下。。。)

Object.getOwnPropertyNames

用于获取对象自身所有的可枚举的属性值,但不包括原型中的属性,然后返回一个由属性名组成的数组。

一般就是数组会多一个length属性,返回的数组结果就会多'length'字符串

总结

本次文章的难点在于 记,把常用的记住,记住什么时候用什么,怎么用就好了

结语

本次的文章到这里就结束啦!♥♥♥读者大大们认为写的不错的话点个赞再走哦 ♥♥♥ 我们一起学习!一起进步!