js学习-forEach和map是否改变原数组?

1,700 阅读3分钟

前言

这几天在复习js数组的所有API,就是路易斯的 这篇文章,推荐看看! 到底怎么回事呢?当我复习到数组的12种遍历方式的时候,发现这么一句话 :

遍历方法(12个)

” 基于ES6,不会改变自身的方法一共有12个,分别为forEachmap、every、some、filter、reduce、reduceRight 以及ES6新增的方法entries、find、findIndex、keys、values。“

注意这个forEachmap!为什么它们不会改变原数组呢?我明明一直记得是可以的啊!

然后我就开始去论证我的观点吧,Let's go(来,si狗😂)。

1:
const objArr1 = [{
    name: 'hn',
    age: 12
}]

const objArr2 = [{
    name: 'wx',
    age: 13
}]

objArr1.forEach(item => {
    if (item.name === 'hn') {
      item.age = 82
    }
})
objArr2.map(item => {
    if (item.name === 'wx') {
      item.age = 83
    }
})
console.log(objArr1);  //[ { name: 'hn', age: 82 } ] 原数组被改变
console.log(objArr2);  //[ { name: 'wx', age: 83 } ] 原数组被改变

论证结果

可以看出forEach和map确实能改变原数组啊!你以为这就完了?当然以我严谨的性格不可能就此结束的😄 我又在网上查询发现也有很多人遇到这个问题,恍然大悟!原来是我忽略了基本类型和引用类型!以为我平时用到forEavh和map基本都是引用数据类型(例如对象,数组等)。

举例分析

数据为基本类型

  • 改变其值(不会改变)
const array = [1, 2, 3, 4]; 
array.forEach(item => { 
    item = item * 3 
}) 
console.log(array); // [1,2,3,4]

数据为引用类型

  • 改变其属性方法的值(会改变)
例子就是上面的例1!
  • 改变整个单次循环的item (不会改变)
const changeItemArr = [{
    name: 'hn1',
    age: 12
}, {
    name: 'hn2',
    age: 13
}]
changeItemArr.forEach(item => {
    if (item.name === 'hn2') {
        item = {
            name: 'hn3',
            age: 77
        }
    }
})
console.log(changeItemArr);  // [{name: "hn1", age: 12},{name: "hn2", age: 13}]

总结 JavaScript是有基本数据类型与引用数据类型之分的。对于基本数据类型:number,string,Boolean,null,undefined它们在栈内存中直接存储变量与值。而Object对象的真正的数据是保存在堆内存,栈内只保存了对象的变量以及对应的堆的地址,所以操作Object其实就是直接操作了原数组对象本身。

而在forEachmap方法中操作objArr1objArr2,实际操作的是该对象在堆内存地址,由于是对该地址所在的对象进行字段值修改,所以数组里的obj对象相应改变。

其实这让我联想到了另一个知识点,浅拷贝深拷贝浅拷贝就是对引用对象地址的拷贝,拷贝对象的改变也会引起被拷贝对象的改变。所以说知识点有时候是相通的🤗️。

扩展

常用解决方法

// 可以改变基本类型
const numArr = [13,4,15];
numArr.forEach((item, index, arr) => {
    if (item === 13) {
        arr[index] = 99
    }
})
console.log(numArr);  // [99, 4, 15]

// 也可以改变引用类型
const allChangeArr = [{
    name: 'hn1',
    age: 12
}, {
    name: 'hn2',
    age: 13
}]
allChangeArr.forEach((item, index, arr) => {
    if (item.name === 'hn2') {
        arr[index] = {
            name: 'hn3',
            age: 99
        }
    }
})
console.log(allChangeArr); // // [{name: "hn1", age: 12},{name: "hn3", age: 99}]

解析 你也许疑惑为什么通过arr[index]就可以改变不管是基本还是引用类型的原数组了,原因是当调用forEachmap方法参数fn传参是(item,index,arr),你是否还记得它们的参数fn的参数包含三个分别是currentValue(必需。当前元素)index(可选。当前元素的索引值)arr(可选。当前元素所属的数组对象),重点就在arr,它就是原数组啊,所有它自己还没权利改变自己吗?骚嘎寺内!😏