一、 for循环和forEach对比
# 一、 for循环和forEach对比
- for循环是最常规最通用的一种循环遍历方法,后面要讲到的map、filter等都是可以通过for循环完成的;
- forEach方法是一个高阶函数,会引入额外的函数调用开销;
注意:这种微小的性能差异通常在大多数应用中并不明显,除非在处理非常大的数据集时才可能显现出来。在实际开发中,应该优先考虑代码的可读性和维护性,而不是过度关注微小的性能差异。
# 1.性能上的比较
for > forEach
for循环直接操作索引,没有额外的函数调用和上下文,所以性能是最快的
for可以使用break终止,forEach不支持跳出循环
虽然可以在forEach内部可以使用return,满足条件使用return可以结束当前这次循环,但是剩下的循环还是继续跑完。
# 2.异步同步化的支持度
在一些场景下,后端给前端提供的接口不提供批量查询,那么前端就需要将要查询的ID放置到数组中,通过循环遍历查询接口,前端最后组装并展现
以jsonplaceholder接口为例
let arrs = Array.from({length:3},(_,index)=>index+1);
let datas=[];
arrs.forEach(async item=>{
const {data} = await uni.request({
url:"http://jsonplaceholder.typicode.com/posts/"+item
})
datas.push(data)
})
console.log(datas)
注意:这里的操作是为了做验证用的,如果真正要完成以上操作的话建议使用map+Promise.all来完成,在后面的map讲解中会给大家介绍到。
在上面代码中uni.request返回promise对象,使用async/await是可以实现异步等待的,按照预想应该是forEach后面打印datas是一个数组对象,包含三次接口返回的数组,但是最好打印结果是空数组,说明在forEach函数内,不支持await异步等待。
可以将上面forEach代码改造成for循环:
async function fetchData() {
let arrs = Array.from({length:3},(_,index)=>index+1);
let datas=[];
for (let i = 0; i < arrs.length; i++) {
const {data} = await uni.request({
url:"http://jsonplaceholder.typicode.com/posts/"+arrs[i]
});
datas.push(data);
}
console.log(datas);
}
fetchData();
改造成for循环之后,是可以按照预期拿到数组集合的,但是这种查询效率会非常低
按照真实的场景,看下面map+Promise.all的实现方法
# 二、map()方法
map() 方法是数组原型的一个函数,对数组遍历不破坏原数组,将会创建一个新数组,按照原始数组元素顺序依次执行给定的函数,map方法非常适合用于处理数组中的每个元素并生成新的数组
# 语法
- map(callbackFn)
- map(callbackFn, thisArg)
# 参数
callbackFn
- element 数组中当前正在处理的元素。
- index 正在处理的元素在数组中的索引。
- array 调用了 map() 的数组本身。
thisArg
- 执行 callbackFn 时用作 this 的值。
# 用法
1.将数组内每个元素×2后,获取新数组
let arrs == [1,2,3,4]
let newArrs = arrs.map(item => item * 2);
console.log(newArrs);
// [2, 4, 6, 8]
2.将数组对象内每个元素的名称拿出来,作为一个新数组
let arrs = [{name:"华为",price:6999},{name:"苹果",price:9888},{name:"小米",price:4999}]
let newArrs = arrs.map(item => item.name)
console.log(newArrs);
// ['华为', '苹果', '小米']
3.将数组中原来的name不做任何改变,原有price:6999改为price:“6999元”,并且新增number属性值为888
let arrs = [{name:"华为",price:6999},{name:"苹果",price:9888},{name:"小米",price:4999}]
let newArrs = arrs.map(item => {
return{
...item,
price:item.price+'元',
number: 888
}
})
console.log(newArrs);
//[{name: '华为', price: '6999元', number: 888},....]
返回的是每一个对象,要是想简化成一行,必须要将对象用括号包裹起来,如下所示:
let arrs = [{name:"华为",price:6999},{name:"苹果",price:9888},{name:"小米",price:4999}]
let newArrs = arrs.map(item=>({...item,price:item.price+"元",number:888}));
console.log(newArrs);
4.将原数组中属性进行更换,配合对象解构,可以更直观
let arrs = [{key:0,content:"篮球"},{key:1,content:"足球"},{key:2,content:"排球"}];
let newArrs = arrs.map(({key,content})=>({value:key,text:content}));
console.log(newArrs);
// [{value: 0, text: '篮球'},{value: 1, text: '足球'},{value: 2, text: '排球'}]
5.将异步请求map到新数组中,使用Promise.all同时处理多个Promise
下面这个代码块,是解决for循环异步同步化性能消耗过大的问题。
let arrs = Alet arrs = Array.from({length:3},(_,index)=>index+1);
const promises = arrs.map(async (num) =>{
const result = await uni.request({
url:"http://jsonplaceholder.typicode.com/posts/"+num
})
return result;
})
Promise.all(promises).then(res => {
console.log(res)
})
Promise.all请求,network请求的状况,会同时发送请求,会节省很多的时间,for循环异步同步化出现的网络请求状态,会等待一个完成之后再请求另一个,所以时间上会更慢一些
# 三、filter()方法
filter()过滤方法,会对原数组中的每个元素应用指定的函数,并返回一个新数组,其中包含符合条件的元素。原数组不会受到影响。
# 语法
map(callbackFn) map(callbackFn, thisArg)
# 参数
callbackFn
element 数组中当前正在处理的元素。
index 正在处理的元素在数组中的索引。
array 调用了 map() 的数组本身。
thisArg
- 执行 callbackFn 时用作 this 的值。
# 用法
在filter回调函数中,满足true即可被处理到新函数中,false不做处理。
1.找出数组中大于10的数据放到新数组中
let arrs = [5,7,8,15,22,1,2];
let newArrs = arrs.filter(item =>{
return item>10
})
console.log(newArrs);
// [15,22]
如果是数组对象的话也是一样的,只需要找到满足的对象属性即可,比如找到数组对象中,年龄小于30岁的数据:
let arrs = [{name:"张三",age:16},{name:"李四",age:40},{name:"王五",age:28},{name:"汤姆",age:20}];
let newArrs = arrs.filter(item=>{
return item.age<30
})
console.log(newArrs);
//[{name:"张三",age:16},...]
2.数组去重,结合indexOf方法
let arrs = [6,1,2,3,5,3,6];
let newArrs = arrs.filler((item,index,self) => {
return self.indexOf(item) === index
})
console.log(newArrs);
// [6, 1, 2, 3, 5]
3.数组对象中过滤掉无用数据
let arrs = [{id:1,name:"HTML5"},{id:2,name:"JavaScript"},{id:null,name:"小程序"},{name:"NodeJS"},{id:3,name:"VueJS"}];
let newArrs = arrs.filter(item=>{
return item.id
})
console.log(newArrs);
// [ {id: 1, name: 'HTML5'},...]
4.filter和map组合使用,可以使用链式写法;首先去除无用数据,然后给每一条增加一个属性author
let arrs = [{id:1,name:"HTML5"},{id:2,name:"JavaScript"},{id:null,name:"小程序"},{name:"NodeJS"},{id:3,name:"VueJS"}];
let newArrs = arrs.filter(item=>{
return item.id
}).map(item=>{
return {
...item,
author:"咸虾米"
}
})
console.log(newArrs);
// [{id: 1, name: 'HTML5', author: '咸虾米'},...]
# 四、reduce()方法
reduce() 方法对数组中的每个元素按序执行一个指定方法,每一次运行 reducer 会将先前元素的计算结果作为参数传入。
# 语法
- reduce(callbackFn)
- reduce(callbackFn, initialValue)
# 参数
callbackFn
- prev(必填),上一次调用 callbackFn 的结果。
- current(必填),当前元素的值。
- index(可选),current 在数组中的索引位置。
- array(可选),调用了reduce() 的数组本身。
initialValue
- 第一次调用回调函数时初始值。如果指定初始值,则callbackFn从数组中的第一个值作为current开始执行;如果没有指定,数组中的第一个值是prev参数的值,数组第二个值是current参数的值;如果数组是空,则抛出错误,所以在不知道数组长度情况下可以指定初始值为0,保持逻辑的严谨性。
# 用法
1.数组求和 / 求积
let arrs = [1,2,3,4];
let result = arrs.reduce((prev,current,index) => {
console.log(prev,current,index)
return prev+current;
})
console.log("最终结果:"+result);
从上面结果可以看到,数组内有4位成员,但是函数执行了3次,这是因为没有初始值,所以prev是数组的第一位,current是数组的第二位,索引值也是从1开始的
假设指定了初始值,效果如下
let arrs = [1,2,3,4];
let result = arrs.reduce((prev,current,index)=>{
console.log(prev,current,index);
return prev+current;
},0)
console.log("最终结果:"+result);
2.求最大值/最小值
求数组中最大值:
let arrs = [5,6,1,22,3,7];
let result = arrs.reduce((prev,current,index) => {
return Math.max(prev,current);
},0)
console.log(result);
// 22
3.对数组对象处理
数组对象求和,求数组对象中age的和:
let arrs = [{name:"张三",age:29},{name:"李四",age:16},{name:"王五",age:50},{name:"小明",age:21}];
let result = arrs.reduce((prev,current,index)=>{
return prev+current.age
},0)
console.log(result);
// 116
# 五、every()方法
判断数组中所有元素是否满足函数中给定的条件,全部满足返回true,只要有一项不满足则返回false
# 语法
- every(callbackFn)
- every(callbackFn, thisArg)
# 参数
callbackFn
- element 数组中当前正在处理的元素。
- index 正在处理的元素在数组中的索引。
- array 调用了 every() 的数组本身。
thisArg
- 执行 callbackFn 时用作 this 的值。
# 用法
1.检查数组中的所有元素是否满足特定条件
let arrs = [1, 2, 3, 4, 5];
let result = arrs.every(num => num > 0);
console.log(result);
//true
2.检测数组对象中,是否所有商品都有库存
let arrs = [{name:"华为",price:5899,stock:true},{name:"苹果",price:9999,stock:false},{name:"小米",price:4399,stock:true},{name:"红米",price:899,stock:true}];
let result = arrs.every(item=>item.stock);
console.log(result);
// false
3.检测对象内所有属性是否都有值,配合Object.values()
<script setup>
import {computed, ref} from "vue";
const obj = ref({
name:"",
age:"",
gender:"",
like:""
})
// 不使用every遍历
const state = computed(()=>{
if(obj.value.name && obj.value.age && obj.value.gender && obj.value.like){
return false;
}
return true;
})
//使用
const state = computed(()=>{ // 使用Object.values()方法,可以将所有的值放入到一个数组中
return !Object.values(obj.value).every(item=>item)
})
function onSubmit(){
console.log("提交表单");
}
</script>
# 六、some()方法
some方法和every方法基本类似,只是some方法检测数组中,只要有一个满足条件即返回true,全部不满足条件才会返回false。
# 语法
- some(callbackFn)
- some(callbackFn, thisArg)
# 参数
callbackFn
- element 数组中当前正在处理的元素。
- index 正在处理的元素在数组中的索引。
- array 调用了 some() 的数组本身。
thisArg
- 执行 callbackFn 时用作 this 的值。
# 用法
1.检查数组中的是否有满足特定条件的元素
let arrs = [55,26,3,12,39];
let result = arrs.some(item=>item<10);
console.log(result);
// true
2.检查数组对象中,是否有满足price小于1000的元素,并且有stock库存
let arrs = [{name:"华为",price:5899,stock:true},{name:"苹果",price:9999,stock:false},{name:"小米",price:4399,stock:true},{name:"红米",price:899,stock:true}];
let result = arrs.some(item=>item.price<1000 && item.stock);
console.log(result);
// true
3.将every表单示例改造成some方法
1)使用some完成全部对象有值的判断
const state = computed(()=>Object.values(obj.value).some(item=>!item))
2)只要有一个表单有值就将禁用改可用
const state = computed(()=>!Object.values(obj.value).some(item=>item))
# 七、includes()方法
includes() 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回 false
**注意:**includes() 方法只能用于检测基础数据类型(如数字、字符串、布尔值),而不能用于检测包含对象或其他数组的二维数组。如果需要检测对象或其他复杂数据类型,可以使用其他方法或自定义函数来实现
# 用法
1.基础用法
const arrs = [1, 2, 3];
console.log(arrs.includes(2));
// true
2.every和includes配合,检测一个数组是否包含另一个数组
let arrs1 = [1,2,3,4,5,6,7];
let arrs2 = [2,5,7];
let arrs3 = [1,6,9];
let result1 = arrs2.every(item=>arrs1.includes(item));
let result2 = arrs3.every(item=>arrs1.includes(item));
console.log(result1);
console.log(result2);
// true
// false
# 八、for…in 、for…of
for...in
循环读取键名,(遍历对象)for...of
循环读取键值,(遍历带有iterator接口的Set,Map,String,Array)
如果要通过for...of
循环,获取数组的索引,可以借助数组实例的entries
方法和keys
方法。
let list = [4, 5, 6];
for (let i in list) {
console.log(i + '-' + list[i]); // 打印: 0-4,1-5,2-6
}
for (let i of list) {
console.log(i); // 打印: 4,5,6
}
九、