JavaScript 回顾(一)map() 与 forEach(
2019-05-12 本文已影响0人
MercuryWang
1. 关于 map() 与 forEach() 的区别
参考文章援引自 FCC The Differences Between forEach() and map() that Every Developer Should Know
探讨两者区别主要从以下几点展开:
- Definitions 定义
- The returning value 返回值
- Ability to chain other methods 链式操作其他方法
- Mutability 是否改变原数组
- Performance Speed 执行效率
- Final Thoughts 应用场景的思考
1.1 Definitions 定义
- map() 方法的参数是一个函数,然后针对数组的每一个成员执行该函数,最后返回一个全新的数组,该数组由调用函数执行后的结果所填充。也就意味着,新数组的成员是原数组的新形象,新数组的长度与原数组相同。
const myAwesomeArray = [5, 4, 3, 2, 1]
myAwesomeArray.map(x => x * x)
// Output: [25, 16, 9, 4, 1]
- forEach() 同样接收一个函数作为参数,且对每一个数组成员执行该函数。不过,它并不像 map() 那样会返回一个新数组,它的返回值是
undefined
const myAwesomeArray = [
{ id: 1, name: "john" },
{ id: 2, name: "Ali" },
{ id: 3, name: "Mass" },
]
myAwesomeArray.forEach(element => console.log(element.name))
// >>>>>>>>> Output : john
// Ali
// Mass
1.2 The returning value 返回值
返回值上面的定义已经提及了,map() 是返回一个新数组,forEach() 返回 undefined
const myAwesomeArray = [1, 2, 3, 4, 5]
myAwesomeArray.forEach(x => x * x)
//>>>>>>>>>>>>>return value: undefined
myAwesomeArray.map(x => x * x)
//>>>>>>>>>>>>>return value: [1, 4, 9, 16, 25]
1.3 Ability to chain other methods 链式操作其他方法
是否可以链式操作是基于返回值的,所以 map() 可以链式调用其他方法,forEach() 不可以。
const myAwesomeArray = [1, 2, 3, 4, 5]
myAwesomeArray.forEach(x => x * x).reduce((total, value) => total + value)
//>>>>>>>>>>>>> Uncaught TypeError: Cannot read property 'reduce' of undefined
myAwesomeArray.map(x => x * x).reduce((total, value) => total + value)
//>>>>>>>>>>>>> return value: 55
1.4 Mutability 是否改变原数组
这个才是重点,改变还是不改变原数组。
mutable object
可变对象的定义:该对象创建后其状态可以改变即为可变对象。
根据 MDN 文档的解释:
- forEach() 不会改变数组对象(但回调函数会改变数组)
- map() 不会改变数组对象(尽管如此,回调函数如果有援引,可能会改变原数组)
感觉等于没说,难道是没有区别吗???
不,当然有。区别就在于 map() 方法返回一个全新的数组,这个新数组是由原数组变形衍生来的;而 forEach() 返回的是 undefined
,所以是对原数组进行了改变操作。
结论:map() 调用回调函数也不会改变原数组,forEach() 如果调用了回调函数会改变原数组。
1.5 Performance Speed 执行效率
执行效率上有细微差别。可执行以下代码查看:
const myAwesomeArray = [1, 2, 3, 4, 5]
const startForEach = performance.now()
myAwesomeArray.forEach(x => (x + x) * 10000000000)
const endForEach = performance.now()
console.log(`Speed [forEach]: ${endForEach - startForEach} miliseconds`)
const startMap = performance.now()
myAwesomeArray.map(x => (x + x) * 10000000000)
const endMap = performance.now()
console.log(`Speed [map]: ${endMap - startMap} miliseconds`)
以我的电脑为例:
Speed [forEach]: 0.02500001573935151 miliseconds
Speed [map]: 0.01999997766688466 miliseconds
1.6 Final Thoughts 应用场景的思考
- 如果是考虑修改或使用数据,可以使用 map(),因为会返回一个新的数组
- 不使用数据的情况,推荐 forEach()
2. All methods 所有数组实例方法
有时候面试会被问到,有哪些数组实例的方法会改变原数组,哪些不会。数组 prototype 的所有方法如下:
console
输入 Array.prototype
2.1 改变原数组的方法
方法名 | 功能 | 返回值 |
---|---|---|
unshift |
首部添加 | 新数组长度 |
push |
尾部添加 | 新数组长度 |
shift |
首部删除 | 被删除元素 |
pop |
尾部删除 | 被删除元素,空数组返回 undefined
|
splice(i, c, [n, m]) |
通过删除或替换现有元素,或者原地添加新的元素来修改数组 | 被删除元素组成的数组 |
sort |
用原地算法对数组的元素进行排序,默认 Unicode 位点进行排序 | 排序后的数组 |
reverse |
将数组中元素的位置颠倒 | 颠倒后的数组 |
forEach |
对数组的每个元素执行一次提供的函数 | undefined |
copyWithin(target[, start[, end]]) |
浅复制数组的一部分到同一数组中的另一个位置 | 改变后的数组 |
fill(val, [start, end)) |
用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。 | 修改后的数组 |
includes |
判断一个数组是否包含一个指定的值 | 包含则返回 true ,否则返回 false
|
2.2 不改变原数组的方法
方法名 | 功能 | 返回值 |
---|---|---|
concat |
用于合并两个或多个数组 | 返回新数组 |
entries |
返回一个新的Array Iterator对象 | 该对象包含数组中每个索引的键/值对,需要使用 iterableObj.next().value
|
keys |
返回一个新的 Array Iterator 对象 |
该对象包含数组中每个索引键 |
values |
返回一个新的 Array Iterator 对象 |
该对象包含数组中每个索引的值 |
every |
测试一个数组内的所有元素是否都能通过某个指定函数的测试 | 布尔值 |
filter |
创建一个新数组, 其包含通过所提供函数实现的测试的所有元素 | 一个新的、由通过测试的元素组成的数组,如果没有任何数组元素通过测试,则返回空数组 |
find |
查找满足条件的第一个元素 | 返回元素的值,若无返回 undefined
|
findIndex |
查找满足条件的第一个元素 | 返回元素的索引,若无返回 -1 |
flat(depth) |
扁平化嵌套数组或移除数组空项 | 一个包含将数组与子数组中所有元素的新数组 |
flatMap |
与 arr.map(callback).flat(1) 几乎相同 |
一个新的数组,其中每个元素都是回调函数的结果,并且depth 值为1 |
indexOf(searchE, fromIndex) |
比 findIndex 多一个参数,也是查找元素 |
返回元素的索引,若无返回 -1 |
lastIndexOf |
最后一个索引 | 返回元素的索引,若无返回 -1 |
join(separator) |
将一个数组或一个类数组对象的所有元素连接成一个字符串 | 返回拼接的字符串 |
map |
创建一个符合回调函数的数组 | 回调函数的结果 |
slice([begin, end)) |
一个由 begin 和 end 决定的原数组的浅拷贝 | 一个含有被提取元素的新数组 |
3. 其他举例
例(1)模糊查询,String 的 includes
结合 Array 的 filter
示例:
var strs = ["back", "to", "basics"];
console.log(strs.filter(str => str.includes("a"))); // ["back", "basics"]
例(2)如下 JavaScript 代码运行后,b、c 的值分别是?
var a = ["monkey", "elephant", "horse"];
var b = a;
var c = a.slice();
a.push("panda");
答案:
b = ["monkey", "elephant", "horse", "panda"];
c = ["monkey", "elephant", "horse"];
a、b 指向同一个引用地址,c 的指向是 slice 创建的新数组。