🐱 个人主页:不叫猫先生,公众号:前端Clodplay
🙋♂️ 作者简介:前端领域优质作者、阿里云专家博主,共同学习共同进步,一起加油呀!
✨优质专栏:VS Code插件开发极速入门
📢 资料领取:前端进阶资料可以找我免费领取
目录
- 区别
- 手写 map 和 forEach
- map 和 forEach 会不会改变原数组
- 处理基本类型数据
- 处理引用类型数据
- 思考:为什么基础数据类型不会更改原数据,引用类型不会
区别
map会返回一个新的数组,而forEach不会。具体探讨一下,实则会有新的发现!
下面来探讨一下原因
手写 map 和 forEach
手写map,如下:
function myMap(array, callback) {//存储结果const result = [];for (let i = 0; i < array.length; i++) {result.push(callback(array[i], i, array));}return result;
}
调用上面的myMap
函数
// 定义一个回调函数,用于对数组元素进行操作
function double(value) {return value * 2;
}// 定义一个数组
const numbers = [1, 2, 3, 4, 5];// 调用 myMap 函数,并传入定义好的回调函数
const doubledNumbers = myMap(numbers, double);console.log(doubledNumbers); // 输出 [2, 4, 6, 8, 10]
手写forEach,如下:
function myForEach(array, callback) {for (let i = 0; i < array.length; i++) {callback(array[i], i, array);}
}
调用上面的myForEach
函数
// 定义一个回调函数,用于对数组元素进行操作
function logItem(item, index) {console.log(`Index ${index}: ${item}`);
}// 定义一个数组
const fruits = ["Apple", "Banana", "Orange", "Mango"];// 调用 myForEach 函数,并传入定义好的回调函数
myForEach(fruits, logItem);
总结:
上面可以看到 forEach 和 map 的实现原理相似。它们都是通过遍历数组,对数组的每个元素执行特定的函数。区别主要在于它们处理函数返回值的方式不同。
forEach 忽略函数的返回值,而 map 则将函数的返回值收集到一个新的数组中
可以看到 map 和 forEach都会有一个for循环处理,且map有return
for (let i = 0; i < array.length; i++) {result.push(callback(array[i], i, array));}
for (let i = 0; i < array.length; i++) {callback(array[i], i, array);}
那么能不能forEach也return,测试一下
const array = [1, 2, 3]const result = array.forEach((element) => {return element * 2; // 这个返回值不会被 forEach 捕获或使用});console.log(result); // 输出: undefined
但是map,return 之后是有值的
const array = [1, 2, 3]const result = array.map((element) => {return element * 2; // 这个返回值会被 map 捕获或使用});console.log(result); // 输出: [2, 4, 6]
map 和 forEach 会不会改变原数组
map 和 forEach 会不会改变原数组,需要看数据类型,具体分为基础数据类型和引用类型。
处理基本类型数据
forEach 处理基本数据类型
let array = [1, 2, 3, 4];array.forEach(item => {item = item + 1})console.log(array); // [1,2,3,4]
map 处理基本数据类型
let array = [1, 2, 3, 4];array.map(item => {item = item + 1})console.log(array); // [1,2,3,4]
测试后发现,都没有改变原数组
处理引用类型数据
forEach 处理引用类型数据
const arr = [{name: 'shaka',age: 23}, {name: 'virgo',age: 18}]arr.forEach(item => {if (item.name === 'shaka') {item.age = 100}})console.log(arr); //[{name: 'shaka', age: 100}, {name: 'virgo', age: 18}]
map 处理引用类型数据
const arr = [{name: 'shaka',age: 23}, {name: 'virgo',age: 18}]arr.map(item => {if (item.name === 'shaka') {item.age = 100}})console.log(arr); //[{name: 'shaka', age: 100}, {name: 'virgo', age: 18}]
发现都改变了原数据
思考:为什么基础数据类型不会更改原数据,引用类型不会
这是因为在使用 forEach 和 map 方法时,对引用类型元素的修改会直接反映在原始数组中。这是因为引用类型的元素实际上存储的是引用(内存地址),而非值本身。因此,通过引用可以访问和修改原始数组中的元素。而number、string、Boolean、null、undefined它们在栈内存中直接存储变量与值。
具体解释如下:
基本数据类型为值传递,callback的执行和原数组毫不相干,所以forEach无法修改基本数据的数组
基本数据类型(Primitive data types)如数值(Number)、字符串(String)、布尔值(Boolean)、null 和 undefined 等,它们在内存中是直接存储变量的值,而不是引用。因此,对于基本数据类型,当它们作为数组的元素被传递给 forEach 或 map 方法时,处理的是它们的值的拷贝,而不是原始值本身。因此,对于基本数据类型,任何更改都只是在处理的拷贝上进行,不会影响到原始数组中的值。
引用类型(Reference types)如对象(Object)、数组(Array)和函数(Function)等,它们在内存中存储的是地址,即指向存储实际数据的内存地址。因此,当引用类型的元素被传递给 forEach 或 map 方法时,处理的是对该地址的引用,而不是原始数据本身。这意味着对引用类型的元素进行的任何更改都会直接反映在原始数组中,因为它们都指向相同的内存地址。