一、循环遍历
for循环,也是最常见的。
可以使用临时变量,将长度缓存起来,避免重复获取数组长度,当数组较大时优化效果才会比较明显。
// arr 是要遍历的数组
// arr[i] 是遍历的数组的元素
// i 是数组的元素对应的下标(索引号)
for (var i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
let arr = [1, 2, 3, 4];
for (let i = 0; i < arr.length; i++) {
arr[i] = arr[i] + 1; // 直接通过索引修改原数组的值
}
console.log(arr); // [2,3,4,5]
let list = [{sex: '男'},{sex: '女'},{sex: '男'}];
for (let i = 0; i < list.length; i++) {
list[i].sex = '女';
}
console.log(list); // [{sex: '女'}, {sex: '女'}, {sex: '女'}]
二、for of遍历
// arr 遍历的数组
// item 遍历的数组的元素
for(var item of arr) {
console.log(item);
}
let arr = [1, 2, 3, 4];
for(var item of arr) {
console.log(item);
} // 1 2 3 4
三、for in遍历
// arr 遍历的数组
// item 遍历的数组的元素
for(var index in arr) {
console.log(item);
}
let arr = [1, 2, 3, 4];
for(var index in arr) {
console.log(item);
}
// 0 1 2 3
四、forEach()遍历
这种方法只是使处理需要循环的数组变得更加容易,因为你不必像通常那样写出笨重而长的for循环语句。
forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数,没有返回值,原数组不变。
forEach() 对于空数组是不会执行回调函数的。
注意和map方法区分。
// arrObj 需要遍历的数组
// item 遍历出的每一个元素
// index 元素对应的下标
// self 数组本身
// 无返回值
arrObj.forEach(function(item,index,self) {
console.log(item);
});
let arr = [1, 2, 3, 4];
arr.forEach(item => {
item = item * 2;
});
console.log(arr); // [1, 2, 3, 4] 原数组保持不变
五、map()映射遍历
map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值,并没有改变原来的数组。
map() 方法按照原始数组元素顺序依次处理元素。
注意:map() 不会对空数组进行检测。
// arrObj 需要遍历的数组
// item 遍历出的每一个元素
// index 元素对应的下标
// self 数组本身
// 有返回值
// newArr 新返回的数组
// 数组元素个数不变,但是按照一定的条件转换
arrObj.map(function(item,index,self) {
return item*2;
});
let arr = [1, 2, 3, 4];
let newArr = arr.map(item => {
return item * 2;
});
console.log(arr); // [1, 2, 3, 4] 原数组保持不变
console.log(newArr); // [2, 4, 6, 8] 返回新数组
for 与 forEach、map的区别
- 在固定长度或者长度不需要计算的时候for循环效率高于foreach和map,for循环中可以通过break终止。
- 在不确定长度或者计算长度有损性能的时候用foreach和map比较方便
forEach、map的区别
相同点:
- 都是循环遍历数组中的每一项
- forEach和map方法里每次执行匿名函数都支持3个参数,参数分别是item(当前每一项),index(索引值),arr(原数组)
- 匿名函数中的this都是指向window
- 只能遍历数组
- 都不会改变原数组
不同点:
- map方法返回一个新的数组,数组中的元素为原始数组调用函数处理后的值
- forEach方法不会返回新数组
性能:
- for > forEach > map
- for 循环当然是最简单的,因为它没有任何额外的函数调用栈和上下文
- forEach 其次,因为它其实比我们想象得要复杂一些。它不是普通的 for 循环的语法糖,还有诸多参数和上下文需要在执行的时候考虑进来,这里可能拖慢性能;
- map 最慢,因为它的返回值是一个等长的全新的数组,数组创建和赋值产生的性能开销很大。
六、filter()过滤遍历
将所有元素进行判断,将满足条件的元素作为一个新的数组返回, 原数组不变。
注意:filter() 不会对空数组进行检测。
// arrObj 需要遍历的数组
// item 遍历出的每一个元素
// index 元素对应的下标
// self 数组本身
// 有返回值,返回满足某个条件的元素构成的数组
// 数组元素个数可能改变,按照一定的条件返回
arrObj.filter(function(item,index,self) {
return item < 3;
});
let arr = [1, 2, 3, 4];
let newArr = arr.filter(item => {
return item < 3;
});
console.log(arr); // [1, 2, 3, 4] 原数组保持不变
console.log(newArr); // [1, 2] 返回新数组
七、find()遍历
数组中的每个元素都调用一次函数执行。
当数组中的元素在测试条件时返回true,find()返回符合条件的元素,之后的值不会再执行函数;如果没有符合条件的元素则返回undefined,原数组不变。
注意::find() 对于空数组,函数是不会执行的。
let arr = [1, 2, 3, 4];
let newArr1 = arr.find(item => {
return item > 2;
});
let newArr2 = arr.find(item => {
return item > 5;
});
console.log(arr); // [1, 2, 3, 4] 原数组保持不变
console.log(newArr1); // 3 返回第一个符合条件的元素
console.log(newArr2); // undefined 没有符合条件的元素则返回undefined
八、findIndex()遍历
遍历数组,找到第一个符合条件的元素,并返回该元素;否则,返回-1。
不改变原数组。
let fruits= ['苹果', '梨子', '桃子', '西瓜', '哈密瓜'];
let vegetables= [{
id: 1,
name: '花椰菜'
},{
id: 2,
name: '青椒'
},{
id: 3,
name: '土豆'
},{
id: 4,
name: '西红柿'
}];
// fruits 需要遍历的数组
// item 数组的元素
// index 元素对应的下标
// arr 数组本身
let findIndexFru = fruits.findIndex(function(item, index, arr){
return index > 5;
});
console.log(findIndexFru); // -1
let findIndexVeg = vegetables.findIndex(function(item, index, arr){
// 返回 遍历子对象
console.log(item.name);
// // 返回 遍历索引值
console.log(index);
// 返回 所有子对象
console.log(arr);
return item.id> 2;
});
console.log(findIndexVeg);
九、indexOf()遍历
从前往后遍历数组,找到第一个符合条件的项,并返回该项。否则返回-1。
不改变原数组。
字符串也有此方法,功能类似。
let fruits= ['苹果', '梨子', '桃子', , '西瓜', '哈密瓜'];
// fruits 需要遍历的数组
// item 需要搜索的值
// index 数组中的的索引值,可以为负数,代表相对数组末尾的偏移量
let indexOfFru = fruits.indexOf('桃子');
console.log(indexOfFru); // 2
十、lastIndexOf()遍历
从后往前遍历数组,找到第一个符合条件的项,并返回该项。否则返回-1。
不改变原数组。
字符串也有此方法,功能类似。
let fruits= ['苹果', '梨子', '桃子', '西瓜', '哈密瓜'];
// fruits 需要遍历的数组
// item 需要搜索的值
// index 数组中的的索引值,可以为负数,代表相对数组末尾的偏移量
let lastIndexOfFru = fruits.lastIndexOf('西瓜');
console.log(lastIndexOfFru); // 3
十一、every()遍历
将所有元素进行判断,返回一个布尔值,原数组不变。
如果所有元素都满足判断条件,则返回true,否则为false。
注意:every() 不会对空数组进行检测。
// arrObj 需要遍历的数组
// item 遍历出的每一个元素
// index 元素对应的下标
// self 数组本身
// 有返回值,检测数组里的每一个值是否满足指定条件,如果有一个值不满足,返回false,剩余的值不再进行检测;如果所有的值都满足,则返回true
arrObj.every(function(item,index,self) {
return item < 3;
});
let arr = [1, 2, 3, 4];
let newArr1 = arr.every(item => {
return item > 2;
});
let newArr2 = arr.every(item => {
return item < 5;
});
console.log(arr); // [1, 2, 3, 4] 原数组保持不变
console.log(newArr1); // false
console.log(newArr2); // true
十二、some()遍历
将所有元素进行判断, 返回一个布尔值, 原数组不变。
如果存在元素满足判断条件,则返回true,若所有元素都不满足判断条件,则返回false。
判断条件注意和every方法区分。
// arrObj 需要遍历的数组
// item 遍历出的每一个元素
// index 元素对应的下标
// self 数组本身
// 有返回值,检测数组里的每一个值是否满足指定条件,如果有一个值满足,返回true,剩余的值不再进行检测;如果所有的值都不满足,则返回false
arrObj.some(function(item,index,self) {
return item < 4;
});
let arr = [1, 2, 3, 4];
let newArr1 = arr.some(item => {
return item > 2;
});
let newArr2 = arr.some(item => {
return item > 5;
});
console.log(arr); // [1, 2, 3, 4] 原数组保持不变
console.log(newArr1); // true
console.log(newArr2); // false
十三、reduce高阶函数(迭代(累加器))
// arrObj 需要遍历的数组
// total 初始值或计算结束后的返回值
// item 遍历出的每一个元素
// index 元素对应的下标
// self 数组本身
// 有返回值,返回计算结束后的total值
arrObj.reduce(function(total,item,index,self){
},初始值);
十四、reduceRight()遍历
接收一个函数作为累加器(accumulator),数组中的每个值(从右到左)开始缩减,最终为一个值。
第二个参数作为第一次调用的a的值。
let arrNum = [2, 4, 6, 8, 10];
// 累加
let sum = arrNum.reduceRight(function(x, y){
return x + y;
});
console.log(sum); // 30
// 累乘
let multiple = arrNum.reduceRight(function(x, y){
return x * y;
});
console.log(multiple); // 3840
// 求最大值
let bigger = arrNum.reduceRight(function(x, y){
return x > y ? x : y;
});
console.log(bigger); // 10
十五、遍历器机制
截止到ES6,JavaScript 已经拥有了数组、对象、Map集合和Set集合这样四种数据结构。
为了统一和简化遍历这四种数据结构的操作,ES6引入了遍历器机制。
ES6 规定,可遍历的对象都具有Symbol.iterator 属性,这个属性指向一个函数,就是当前对象默认的遍历器生成函数。
这个遍历器生成函数大致的模样可以用ES5 语法模拟出来:这个函数返回一个next() 方法,每调用next() 方法,都会返回数据结构的当前成员的信息。
具体来说,就是返回一个包含value和done两个属性的对象。
其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
在ES6 中,已经默认为绝大多数內建的数据结构提供了遍历器,不需要自己去创建。
// keys()方法:默认遍历器,其值为集合中的所有键名。
// values()方法:默认遍历器,其值为集合中的所有值。
// entries()方法:默认遍历器,其值为所有成员的键值对。
const arr = ["A", "B", "C"];
console.log([...arr.keys()]); // [0, 1, 2]
console.log([...arr.values()]); // ["A", "B", "C"]
console.log([...arr.entries()]); // [[0, "A"],[1, "B"],[2, "C"]]
const set = new Set(arr);
console.log([...set.keys()]); // ["A", "B", "C"]
console.log([...set.values()]); // ["A", "B", "C"]
console.log([...set.entries()]); // [["A", "A"],["B", "B"],["C", "C"]]
const map = new Map().set("name", "Tom").set("age", 19);
console.log([...map.keys()]); // ["name", "age"]
console.log([...map.values()]); // ["Tom", 19]
console.log([...map.entries()]); // [["name", "Tom"],["age", 19]]
每个数据结构都有一个默认的遍历器,例如数组的默认遍历器是values(),在没有明确指定遍历器的情况下,这些数据结构都会使用默认的遍历器。
我们可以通过检测对象的Symbol.iterator属性来判断对象是否拥有遍历器。
const arr = ["A", "B", "C"];
console.log(typeof arr[Symbol.iterator] === "function"); // true
console.log(arr[Symbol.iterator]); // function values() { ... }
const set = new Set(arr);
console.log(typeof set[Symbol.iterator] === "function"); // true
console.log(set[Symbol.iterator]); // function values() { ... }
const map = new Map().set("name", "Tom").set("age", 19);
console.log(typeof map[Symbol.iterator] === "function"); // true
console.log(map[Symbol.iterator]); // function entries() { ... }
原生具备遍历器的对象:
数组、Map集合、Set集合、字符串、arguments和 NodeList(节点列表)。
对象(Object)默认是不可遍历的,我们可以通过Object.keys()、Object.values()和Object.entries() 方法把对象变成数组,使其拥有遍历器;或者直接为对象添加Symbol.iterator 属性来自定义遍历器。
const obj = {
name: "Tom",
age: 19
}
console.log(typeof Object.entries(obj)[Symbol.iterator] === "function"); // true
console.log([...Object.entries(obj)]); // [["name", "Tom"],["age", 19]]