你还在以为forEach真的能改变原数组吗?你悟了吗?

前言

一直以来我都把 forEach可以改变原数组 铭记于心,直到昨天,我才发现我被这句话坑了。
平时的开发我都是用 forEach 来对对象数组进行数据处理,一般从后端接收回来的json数据都要先进行数据处理。

举个🌰

// 这里goodsList模拟请求后端返回来的数据
let goodsList =  [{
	id: 1,
	name: 'ozoZai',
	price: 99999999999,
	total: 1,
}, {
	id: 2,
	name: '商品',
	price: 10,
	total: 0,
}]

goodsList.forEach((goods, index) => {
  /* 检验是否无货 */
  goods.soldOut = goods.total === 0 ? true : false
})

console.log(goodsList)

输出的goodsList:

[{
	id: 1,
	name: 'ozoZai',
	price: 99999999999,
	total: 1,
	soldOut: false, // forEach后新加的属性
}, {
	id: 2,
	name: '商品',
	price: 10,
	total: 0,
	soldOut: true, // forEach后新加的属性
}]

从上面的输出可以看出来,forEach的确改变了原来的goodsList数组。

那再看一个🌰,看看forEach改变原数组了吗?

let arr = [1, 2, 3, 4]
arr.forEach(item => {
	item = 0
})
console.log(arr)

你以为打印出来的结果是 [0, 0, 0, 0] 吗?

在这里插入图片描述
哦豁,arr还是原来的arr,咋就没有改变原数组呢?

原理分析

我们看到MDN对Array.prototype.forEach()对解释:

The forEach() method executes a provided function once for each array element.
forEach() 方法对数组的每个元素执行一次给定的函数。

forEach does not directly mutate the object on which it is called but the object may be mutated by the calls to callbackfn.
forEach 不会直接改变调用它的对象,但是那个对象可能会被 callback 函数改变。

再看到forEach的语法:

arr.forEach(callback(currentValue [, index [, array]])[, thisArg])

currentValue: 数组中正在处理的当前元素。

那forEach是怎样拿到这个当前的元素的呢?

实际上forEach只是把当前元素在内存中的值复制给currentValue
这里就会引申到JavaScript的内存空间了。

首先JavaScript的数据分为基本数据类型和引用数据类型。

基本数据类型:Number, String, Boolean, Null, Undefined, Symbol
引用数据类型:Object

内存中的栈 ⬇️

变量
num1
str‘abc’
booltrue
obj0x00ff12

对于基本数据类型,在栈内存中是按值存储,存储的就是真实的数据。
而引用数据类型,在栈内存中存储的是指向该对象的地址,需要通过引用访问的方式才能访问到对象中实际的数据。

回到forEach中,每次的currentValue得到的都是当前元素在栈内存中的值。

当前元素是基本数据类型的时候,currentValue实际上得到的值是该元素的真实的值,相当于是当前元素的副本,与当前元素没有啥关系。

当前元素是引用数据类型的时候,currentValue实际上得到的值是指向该对象的地址,当我们通过currentValue.属性的时候,访问的就是该对象本身。因为currentValue得到了该对象的地址,就相当于currentValue和该对象指向同一个地址,他们实际上是同一个对象,只是名字不同了而已。

所以当forEach操作基本数据类型的时候,并不会改变原数组中的值。而当forEach操作引用数据类型的时候,才会改变原数组中的值。

后续

那问题来了,那我们要对数组中的每一个基本数据类型元素进行修改的时候,forEach就用不着了吗?

别忘了,数组本身也是引用数据类型,所以我们要用forEach来修改基本数据类型的时候,可以通过引用访问的方式来对元素进行修改。

let arr = [1, 2, 3, 4]
arr.forEach((item, index) => {
	arr[index] = 0
})
console.log(arr)  // [0, 0, 0, 0]

通过这样以 数组[下标] 的引用访问形式,就可以实现在forEach中对原数组中的基本数据类型进行修改。

总结

当我们要用forEach对原数组进行操作的时候,需要以引用访问的方式进行访问数据,才能直接修改原数组。

let arr = [1, 'a', true, {a: 1}]
arr.forEach((item, index) => {
	if (item instanceof Object) {
		item.a = 0
	} else {
		arr[index] = 0
	}
})
console.log(arr) // [0, 0, 0, {a:0}]
  • 10
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值