乐趣区

vue 数组遍历方法forEach和map的原理解析和实际应用

一、前言
forEach 和 map 是数组的两个方法,作用都是遍历数组。在 vue 项目的处理数据中经常会用到,这里介绍一下两者的区别和具体用法示例。
二、代码
1. 相同点

都是数组的方法
都用来遍历数组
两个函数都有 4 个参数:匿名函数中可传 3 个参数 item(当前项), index(当前项的索引), arr(原数组),还有一个可选参数 this
匿名函数中的 this 默认是指向 window 的
对空数组不会调用回调函数
不会改变原数组(forEach 可改变数组内的一些值)

2. forEach
这个方法非常耐人寻味。
(1) 没有返回值。
var a = [1,2,3,4,5]
var b = a.forEach((item) => {
item = item * 2
})
console.log(b)
// undefiined
(2) 可以改变原数组也可以不改变原数组
下面来看几个例子:
var a = [1,2,3,4,5]
a.forEach((item) => {
item = item * 2
})
console.log(a)
// [1,2,3,4,5]
这里原数组并没有发生改变。
var a = [1,’1′,{num:1},true]
a.forEach((item, index, arr) => {
item = 2
})
console.log(a)
// [1,’1′,{num:1},true]
这里修改 item 的值,依然没有修改原数组。
var a = [1,’1′,{num:1},true]
a.forEach((item, index, arr) => {
item.num = 2
item = 2
})
console.log(a)
// [1,’1′,{num:2},true]
当修改数组中对象的某个属性时,发现属性改变了。
为什么会这样呢?这里就要引入栈 (stack) 内存和堆 (heap) 内存的概念了,对于 JS 中的基本数据类型,如 String,Number,Boolean,Undefined,Null 是存在于栈内存中的,在栈内存中储存变量名及相应的值。而 Object,Array,Function 存在于堆内存中,在堆内存中储存变量名及引用位置。
在第一个例子中,为什么直接修改 item 无法修改原数组呢,因为 item 的值并不是相应的原数组中的值,而是重新建立的一个新变量,值和原数组相同。在第二个例子中,数组中的对象的值也没有改变,是因为新创建的变量和原数组中的对象虽然指向同一个地址,但改变的是新变量的值,即新对象的值为 2,原数组中的对象还是{num:1}。在第三个例子中,由于对象是引用类型,新对象和旧对象指向的都是同一个地址,所以新对象把 num 变成了 2,原数组中的对象也改变了。
var a = [1,2,3,4,5]
a.forEach((item, index, arr) => {
arr[index] = item * 2
})
console.log(a)
// [2,4,6,8,10]
在回调函数里改变 arr 的值,原数组改变了。
这个例子和例三其实同理,参数中的 arr 也只是原数组的一个拷贝,如果修改数组中的某一项则原数组也改变因为指向同一引用地址,而如果给参数 arr 赋其他值,则原数组不变。
其实想要知道参数中的 item 和 arr 是不是重新创建的变量,在回调函数中打印就知道了。
(3) vue 中的应用
在处理数据时我经常用到这个方法,因为数据的传递以 json 格式,经常会收到数组中包含许多对象的数据。而后端传给我的数据有时候需要处理,例如把时间戳格式化为正常时间,代码如下:
// utils.js
const formatTime = date => {
var newDate = new Date();
newDate.setTime(date * 1000);
const year = newDate.getFullYear()
const month = newDate.getMonth() + 1
const day = newDate.getDate()
const hour = newDate.getHours()
const minute = newDate.getMinutes()
const second = newDate.getSeconds()

return [year, month, day].map(formatNumber).join(‘-‘) + ‘ ‘ + [hour, minute, second].map(formatNumber).join(‘:’)
}

const formatNumber = n => {
n = n.toString()
return n[1] ? n : ‘0’ + n
}

export {
formatTime
}
// 得到的数据格式
[
{add_time: 1541495677, balance: 14, bn: “300708”, cprice: “12.39”}
]
// index.vue
import axios from ‘axios’
import {formatTime} from ‘@/lib/utils’
export default {
data() {
dataList: []
},
methods: {
getData() {
axios.get(‘/user?ID=12345’)
.then(function (res) {
if(res.code == 200) {
res.data.forEach((item) => {
item.add_time = formatTime(item.add_time)
}
this.dataList = res.data
}
})
.catch(function (err) {
console.log(err);
});
}
}
}
这时候原始数据的值也改变了,变成了格式化后的时间。
3. map
(1) 返回一个经过处理后的新数组,但不改变原数组的值。
var a = [1,2,3,4,5]
var b = a.map((item) => {
return item = item * 2
})
console.log(a) // [1,2,3,4,5]
console.log(b) // [2,4,6,8,10]
(2)vue 中的应用
有这样一个需求,充值金额需要在整数的基础上随机减去 100 或加上 100,这时我在原始的数据基础上需要一个经过处理的新数组。
export default {
data() {
moneyList: [1000,2000,5000,10000,20000,50000]
},
computed: {
moneyList_new() {
return this.moneyList.map((item) => {
const random = Math.random() > 0.5 ? 1 : -1;
return Math.floor(Math.random()*100) * random + item;
})
}
}
}
实际渲染处理过的数组就可以了~
三、结语
以上就是 forEach 和 map 的对比与实际应用,代码只是演示使用方法并非完全真实。

退出移动版