JavaScript 中 forEach、map、filter 详细

429 阅读6分钟

没有时间?直接看重点!

1、forEach 和 map 能实现的功能相似
2、forEach 、 map、filter 都能实现对原数组的修改
3、forEach 没有返回值,map 有返回值,filter 有返回值


forEach

forEach() 方法对数组的每个元素执行一次提供的函数。

语法:

array.forEach(callback(currentVal, index, array) {
  // do something
}, thisArg)

forEach 使用说明

1、forEach 方法按升序为数组中含有效值的每一项执行一次 callback 函数,那些已删除(使用 delete 方法等情况)或者未初始化的项将被跳过(但不包括那些值为 undefined 的项)(例如在稀疏数组上)。

2、如果给 forEach 传递了 thisArg 参数,当调用时,它将被传给 callback 函数,作为它的 this 值。否则,将会传入 window 作为它的 this 值。callback 函数最终可观察到 this 值,这取决于 函数观察到 this 的常用规则。

关于 JavaScript 中的 this,我觉得太重要了,需要仔细研读

3、forEach 遍历的范围在第一次调用 callback 前就会确定。调用 forEach 后添加到数组中的项不会被 callback 访问到。如果已经存在的值被改变,则传递给 callback 的值是 forEach 遍历到他们那一刻的值。已删除的项不会被遍历到。如果已访问的元素在迭代时被删除了(例如使用 shift()) ,之后的元素将被跳过。

4、forEach() 为每个数组元素执行 callback 函数;不像 map() 或者 reduce() ,它总是返回 undefined 值,并且不可链式调用。典型用例是在一个链的最后执行副作用。

forEach 要点

1、没有返回值

var arr1 = [1, 2, 3, 4, 5]

var solt = arr1.forEach((v,i,t) => {
	console.log(v)
})

console.log(solt)	// undefined

2、不能中止或跳出 forEach 循环

var arr1 = [1, 2, 3, 4, 5]

// 使用break会报错
arr1.forEach((v,i,arr) => {
  console.log(v)
  if(v === 3) {
    breck
  }
})

// return false 也无效
arr1.forEach((v,i,arr) => {
  console.log(v)
  if(v === 3) {
    console.log('-----')
    return false
  }
})
// 1
// 2
// 3
// -----
// 4
// 5

3、使用箭头函数,thisArg 参数会被忽略

var arr1 = [1, 2, 3]
var arr2 = [7, 8, 9]

arr1.forEach((v, i, arr) => {
  console.log(this)
})
// window
// window
// window

arr1.forEach((v, i, arr) => {
  console.log(this)
}, arr2)
// window
// window
// window

4、forEach()不会在迭代之前创建数组的副本 如果数组在迭代时被修改了,则其他元素会被跳过

var words = ["one", "two", "three", "four"];
words.forEach(function(word) {
  console.log(word);
  if (word === "two") {
    words.shift();
  }
});
// one
// two
// four

当到达包含值 "two" 的项时,整个数组的第一个项被移除了,这导致所有剩下的项上移一个位置。因为元素 "four" 现在在数组更前的位置,"three" 会被跳过。 forEach() 不会在迭代之前创建数组的副本。

5、对原数组进行修改

var arr1 = [1, 2, 3]
var arr2 = [7, 8, 9]

arr1.forEach(function(v, i, arr) {
  console.log(this)
  arr[i] = v * 2
}, arr2)

console.log(arr1)
// (3) [7, 8, 9]
// (3) [7, 8, 9]
// (3) [7, 8, 9]
// (3) [2, 4, 6]

arr1 从 [1, 2, 3] 变成了 [2, 4, 6] 函数内部 this 值是 arr2


map

map() 方法创建一个数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果

语法:

let new_array = arr.map(function(v, i, arr) {
  // Return element for new_array 
}[, thisArg])

返回值:

一个新数组,每个元素都是回调函数的结果

map 使用说明

1、map 不修改调用它的原数组本身(当然可以在 callback 执行时改变原数组)。

2、如果 thisArg 参数有值,则每次 callback 函数被调用的时候,this 都会指向 thisArg 参数上的这个对象。如果省略了 thisArg 参数,或者赋值为 nullundefined,则 this 指向全局对象 。

3、map 方法会给原数组中的每个元素都按顺序调用一次 callback 函数。callback 每次执行后的返回值(包括 undefined)组合起来形成一个新数组。 callback 函数只会在有值的索引上被调用;那些从来没被赋过值或者使用 delete 删除的索引则不会被调用。

4、使用 map 方法处理数组时,数组元素的范围是在 callback 方法第一次调用之前就已经确定了。在 map 方法执行的过程中:原数组中新增加的元素将不会被 callback 访问到;若已经存在的元素被改变或删除了,则它们的传递到 callback 的值是 map 方法遍历到它们的那一时刻的值;而被删除的元素将不会被访问到。【forEach 一样

map 要点

1、querySelectorAll 应用

var elems = document.querySelectorAll('select option:checked');
var values = Array.prototype.map.call(elems, function(obj) {
  return obj.value
});

上面代码展示了如何去遍历用 querySelectorAl 得到的动态对象集合。在这里,我们获得了文档里所有选中的选项,并将其打印。

map 使用技巧案例

通常情况下,map 方法中的 callback 函数只需要接受一个参数,就是正在被遍历的数组元素本身。但这并不意味着 map 只给 callback 传了一个参数。这个思维惯性可能会让我们犯一个很容易犯的错误。

// 下面的语句返回什么呢:
["1", "2", "3"].map(parseInt);
// 你可能觉得会是 [1, 2, 3]
// 但实际的结果是 [1, NaN, NaN]

通常使用 parseInt 时,只需要传递一个参数. 但实际上,parseInt 可以有两个参数,第二个参数是进制数. 可以通过语句 alert(parseInt.length) === 2 来验证. map 方法在调用 callback 函数时,会给它传递三个参数:当前正在遍历的元素,元素索引,原数组本身. 第三个参数 parseInt 会忽视,但第二个参数不会,也就是说: parseInt 把传过来的索引值当成进制数来使用. 从而返回了 NaN.

或者可以使用箭头函数:

['1', '2', '3'].map( str => {
  parseInt(str)
})

一个更简单的方式:

['1', '2', '3'].map(Number);  // [1, 2, 3]
// 与 parseInt 不同,下面的结果会返回浮点数或指数:
['1.1', '2.2e2', '3e300'].map(Number);  // [1.1, 220, 3e+300]

filter

filter() 方法创建一个新数组,其包含通过所提供函数实现的测试的所有元素

语法:

var new_array = arr.filter(callback[, thisArg])

filter 使用说明

1、filter 为数组中的每个元素调用一次 callback 函数,并利用所有使得 callback 返回 true等价于 true 的值 的元素创建一个新数组。callback 只会在已经赋值的索引上被调用,对于那些已经被删除或者从未被赋值的索引不会被调用。那些没有通过 callback 测试的元素会被跳过,不会被包含在新数组中。

2、filter 不会改变原数组,它返回过滤后的新数组。

3、filter 遍历的元素范围在第一次调用 callback 之前就已经确定了。在调用 filter 之后被添加到数组中的元素不会被 filter 遍历到。如果已经存在的元素被改变了,则他们传入 callback 的值是 filter 遍历到它们那一刻的值。被删除或从来未被赋值的元素不会被遍历到。

filter 示例

var filtered = [12, 5, 8, 130, 44].filter(function(v) {
  return v >= 10
})
// [12, 130, 44]

写在最后

我的主页: neveryu.github.io/neveryu

面基QQ群: 685486827