js的遍历
前要
本文也是作者在开leetcode专栏前的前置知识复习巩固 将分类的学习遍历方法。
Iterator(遍历器)
Iterator是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
Iterator 的作用有三个:一是为各种数据结构(Objiect、Array、Set、Map...),提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for...of(for...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'字符串
总结
本次文章的难点在于 记,把常用的记住,记住什么时候用什么,怎么用就好了
结语
本次的文章到这里就结束啦!♥♥♥读者大大们认为写的不错的话点个赞再走哦 ♥♥♥ 我们一起学习!一起进步!