一、 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);

image-20250218205622917

从上面结果可以看到,数组内有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);

image-20250218205728223

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
}

九、