ES5 与 ES6 遍历数组的不同方法

4,751 阅读6分钟

数组遍历的不同方法(ES5 和 ES6)

1. for 循环

一开始就学习的for循环,也是我们最熟悉的遍历数组的方法,简单回忆一下

const arr = [1, 2, 3, 4, 5];  //声明数组
for (let i = 0; i < arr.length; i++) {    //for循环
    console.log(arr[i]);
}

2. forEach 方法

同样的遍历数组,用forEach方法代码如下

const arr1 = [1, 2, 3, 4, 5];  //声明数组
arr1.forEach(function(item) {
    console.log(item)   //item为每一项,不是每一项的索引(切记)
})

//使用箭头函数
arr1.forEach(item => {
    console.log(item)   //item为每一项,不是每一项的索引(切记)
})

这个语法看起来要简洁很多,不需要通过索引去访问数组项,但缺点也很明显,不支持break,continue等

[1,2,3,4,5].forEach(function(i){
  if (i === 3) {
    return;    //return时,打印结果:1,2,4,5
    //break;     //break时,Uncaught SyntaxError: Illegal break statement(语法错误)
    //continue;  //continue时,Uncaught SyntaxError: Illegal continue statement: no surrounding iteration statement
  } else {
    console.log(i)
  }
})

这段代码的 “本意” 是想从第一个元素开始遍历,遇到某一项等于3之后就结束遍历,否则打印出所遍历过的数组项。 可是结果却是 “违背本意” ,因为它的输出为 1,2,4,5

注意:forEach 的代码块中不能使用 break、continue,它会抛出异常。

语法:

array.forEach(function(item, index, arr), thisValue)
参数:
function(item, index, arr) 必需 -----  item: (必需)当前元素  index: (可选) 当前元素的索引值  arr: (可选)当前元素所属的对象
thisValue:  当执行回调函数 function 时,用作 this 的值。
返回值:
undefined (没有返回值)

3. every 方法

everyforEach使用方法一样,不同的是every可以实现 break,continue 那样的效果。

简单的说, return false 等同于 break, return true 等同于 continue, 如果不写, 默认是 return false

[1,2,3,4,5].every(function(i){
  if (i === 3) {
    return false
  } else {
    console.log(i)
    return true  //如果不写,默认是return false 结果为: 1
  }
})
结果: 1, 2

注意: every 的代码块中不能使用 break、continue,它会抛出异常。

语法:

every() 方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。
array.every(function(currentValue,index,arr), thisValue)
返回值:
布尔值。如果所有元素都通过检测返回 true,否则返回 false。
举例:
[32, 33, 16, 40].every(item => {
    return item < 35
})
结果: false

4. filter 方法

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

const words = [1, 3, 4, 8, 5, 2];
const result = words.filter(word => {
  word = word + 3
  return word > 6
});
console.log(result);  // > Array [4, 8, 5]
console.log(words)   // > Array [1, 3, 4, 8, 5, 2]

可以用来筛选一个数组中符合条件的元素

语法:

var newArray = arr.filter(callback(element[, index[, array]])[, thisArg])
参数:
callback: 用来测试数组的每个元素的函数。返回true表示该元素通过测试,保留该元素,false则不保留。 参数与forEach()和every()等相同
thisArg: 与forEach()和every()等相同
返回值:
一个新的、由通过测试的元素组成的数组,如果没有任何数组元素通过测试,则返回空数组。

5. map 方法

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

const array1 = [1, 4, 9, 16];
const map1 = array1.map(function(item) {
    return item * 2
})
//箭头函数
// const map1 = array1.map(item => item * 2);

console.log(map1);   // 输出:[2, 8, 18, 32]
console.log(array1);  //输出:[1, 4, 9, 16]

根据上述代码可以看出 map()不会改变原数组,会返回一个新数组。下面举一个我经常在敲代码时用map()实现的 看起来改变了原数组的例子

const personList = [
    {name: 'aaa', age: 18},
    {name: 'bbb', age: 23},
    {name: 'ccc', age: 25}
]
// 看起来改变了原数组--修改(添加)对象数组中对象的某个属性
personList.map(item => {  //使用forEach()也可以实现  将map替换为forEach
    item.age += 1 // 修改
    item.gender = '男'  //添加
})
console.log(personList)
输出:
[
    {name: "aaa", age: 19, gender: "男"},
    {name: "bbb", age: 24, gender: "男"},
    {name: "ccc", age: 26, gender: "男"}
]

原数组为什么看起来改变了呢?? 因为对象是引用类型,内存地址不变,这个对象就是不变的,而对对象的属性进行操作不会改变内存地址,所以原数组是没改变的

那用map()新生成的对象数组和原对象数组是不是浅拷贝呢???

const personList = [
    {name: 'aaa', age: 18},
    {name: 'bbb', age: 23},
    {name: 'ccc', age: 25}
]
const newList = personList.map(item => {
    item.age += 1
    return item
})
console.log(newList)      //[{name: "aaa", age: 19},{name: "bbb", age: 24},{name: "ccc", age: 26}]
console.log(personList)  //[{name: "aaa", age: 19},{name: "bbb", age: 24},{name: "ccc", age: 26}]

新数组和原数组看起来一样,看一下改变新数组 ,原数组会改变吗?
newList[0].name = '哈哈哈哈哈哈'

console.log(newList)      //[{name: "哈哈哈哈哈哈", age: 19},{name: "bbb", age: 24},{name: "ccc", age: 26}]
console.log(personList)  //[{name: "哈哈哈哈哈哈", age: 19},{name: "bbb", age: 24},{name: "ccc", age: 26}]

根据结果可以看到,修改新数组会改变原数组

如何才能实现修改新数组不改变原数组呢????(使用 map 重新格式化数组中的对象)

const personList = [
    {name: 'aaa', age: 18},
    {name: 'bbb', age: 23},
    {name: 'ccc', age: 25}
]
const newList = personList.map(item => {
    const rObj = {}
    rObj.name = item.name
    rObj.age = item.age
    return rObj
})
console.log(newList)      //[{name: "aaa", age: 18},{name: "bbb", age: 23},{name: "ccc", age: 25}]
console.log(personList)  //[{name: "aaa", age: 18},{name: "bbb", age: 23},{name: "ccc", age: 25}]

newList[0].name = '哈哈哈哈哈哈'

console.log(newList)      //[{name: "哈哈哈哈哈哈", age: 18},{name: "bbb", age: 23},{name: "ccc", age: 25}]
console.log(personList)  //[{name: "aaa", age: 18},{name: "bbb", age: 23},{name: "ccc", age: 25}]

ok,这样改变新数组就不会改变原数组了

语法和forEach()类似

6. for ... in

(for ... in是为遍历对象属性而构建的,不建议与数组一起使用)

var obj = {a:1, b:2, c:3};
    
for (var prop in obj) {
  console.log("obj." + prop + " = " + obj[prop]);
}
// Output:
// "obj.a = 1"
// "obj.b = 2"
// "obj.c = 3"

7. for ... of (重头戏)

for...of循环相比上面几种做法,有一些显著的优点。

for (let val of [1,2,3]) {
  console.log(val);
}
    // 1,2,3
  • 有着同for...in一样的简洁语法,但是没有for...in那些缺点。
  • 不同于forEach方法,它可以与break、continue和return配合使用。
  • 提供了遍历所有数据结构的统一操作接口。
for (variable of iterable) {
    //statements
}

示例

迭代数组

let iterable = [10, 20, 30];

for (let value of iterable) {
    value += 1;
    console.log(value);
}
// 11
// 21
// 31

迭代String

let iterable = "boo";

for (let value of iterable) {
  console.log(value);
}
// "b"
// "o"
// "o"

迭代Map

let iterable = new Map([["a", 1], ["b", 2], ["c", 3]]);

for (let entry of iterable) {
  console.log(entry);
}
// ["a", 1]
// ["b", 2]
// ["c", 3]

for (let [key, value] of iterable) {
  console.log(value);
}
// 1
// 2
// 3

迭代Set

let iterable = new Set([1, 1, 2, 2, 3, 3]);

for (let value of iterable) {
  console.log(value);
}
// 1
// 2
// 3

迭代arguments 对象

(function() {
  for (let argument of arguments) {
    console.log(argument);
  }
})(1, 2, 3);

// 1
// 2
// 3

阮一峰老师的ES6介绍

MDN对for of的介绍和与for in的对比