数组遍历的几种方式
1. for循环
// 最简单的一种循环遍历方法,也是使用频率最高的一种(可优化);循环过程中支持修改索引(修改 i)
var arr = [1, 2, 3, 4, 5, 6]
for(var i = 0; i < arr.length; i++) {
console.log("普通for循环",arr[i]) //1 2 3 4 5 6
}
// 优化:使用临时变量,将长度缓存起来,避免重复获取数组长度,当数组较大时优化效果才会比较明显
var arr = [1, 2, 3, 4, 5, 6]
// var len = arr.length
// for(var i = 0; i < len; i++) {
// console.log("优化后for循环",arr[i]) //1 2 3 4 5 6
// }
for(var i = 0,len=arr.length; i < len; i++) {
console.log("优化后for循环",arr[i]) //1 2 3 4 5 6
}
// 能正确响应break、continue和return语句
2. for…in…
// 这个循环效率比较低,适用于少量数据,也有不少人使用
// 遍历数组,输出的 key 是数组索引
var arr = ['我', '是', '谁', '我', '在', '哪']
for(let key in arr) {
console.log("for...in...遍历数组key",key) // 0 1 2 3 4 5
console.log("输出数组中key对应的值",arr[key])
}
// 遍历对象,输出的则是对象的属性名
let obj = { a: 11, b: 22, c: 33 }
for(let key in obj) {
console.log("for...in...遍历对象key",key) // a b c
console.log("输出对象中key对应的值",obj[key])
}
// 能正确响应break、continue和return语句
3. for…of…(ES6)
// 虽然性能要好于 for..in...,但仍然比不上普通的 for 循环
// 注意:不能循环对象,因为任何数据结构只要部署 Iterator接口,就可以完成遍历操作,有些数据结构原生具备 Iterator 接口,比如Array、Map、Set、String等,而 Iterator 接口是部署在数据结构的Symbol.iterator属性上的,而对象Object恰恰是没有Symbol.iterator属性的,所以无法被for..of遍历
// 遍历数组,输出的 key 是数组元素值---不能循环对象
var arr = ['我', '是', '谁', '我', '在', '哪']
for(var key of arr) {
console.log("for…of…遍历数组key",key) // 我 是 谁 我 在 哪
}
// for-of遍历Map对象
var iterable = new Map([[ "a" , 1], [ "b" , 2], [ "c" , 3]]);
for ( let [key, value] of iterable) {
console.log("for-of遍历Map对象",value); //输出 1 2 3
}
// for-of遍历字符串
var iterable = "china中国" ;
for ( let value of iterable) {
console.log("for-of遍历字符串",value); //输出 "c" "h" "i" "n" "a" "中" "国"
}
// 能正确响应break、continue和return语句
4. Object.keys()
// 上面的方法,注重点都是数组的元素或者对象的属性值。
// 如果单纯的想获取对象的属性名,js有原生的Object.keys()方法(低版本IE不兼容),返回一个由对象的可枚举属性名组成的数组:
/****Object.keys()返回键名数组****/
//数组类型
var arr = [ "a" , "b" , "c" ];
console.log(Object.keys(arr)); // (3) ['0', '1', '2']
//类数组对象
let anObj = { 100: 'a' , 2: 'b' , 7: 'c' };
console.log(Object.keys(anObj)); // (3) ['2', '7', '100']
//一般对象
let xyz = {z: "zzz" , x: "xxx" , y: "yyy" };
console.log(Object.keys(xyz)); // (3) ["z", "x", "y"]
// javascript原生遍历方法的建议用法:
// 用for循环遍历数组
// 用for-in遍历对象
// 用for-of遍历类数组对象(ES6)
// 用Object.keys()获取对象属性名的集合
5. forEach()–是JavaScript(ES5)的方法
// 1. 数组里的元素个数有几个,该方法里的回调就会执行几次
// 2. 第一个参数是数组里的元素,第二个参数为数组里元素的索引,第三个参数则是它自己(利用第三个参数可以进行数组去重)
var arr = [1, 2, 3, 4, 5, 6]
arr.forEach((item, index, array) => {
console.log("forEach入参item",item) // 1 2 3 4 5 6
console.log("forEach入参index",index) // 0 1 2 3 4 5
console.log("forEach入参array",array) // [1, 2, 3, 4, 5, 6]
})
// 3. 数组自带的遍历方法,foreach在循环次数未知或者计算起来较复杂的情况下效率比for循环高
// 4. 循环的数组元素是基本数据类型,不会改变原数据的数据;循环的数组元素为对象,会改变原数组的对象属性的值
// 基本数据类型:undefined、null、Boolean、Number、String;
// 复杂数据类型:Object(Object、Array、Function);
// 循环的数组元素是基本数据类型,不会改变原数据的数据
var arr1 = [1, 2, 3, 4, 5, 6]
arr1.forEach((item) => {
item = 10
})
console.log("循环数组元素是基本数据类型,不改变原数据",arr1) // [1, 2, 3, 4, 5, 6]
// 循环的数组元素为对象,会改变原数组的对象属性的值
var arr2 = [
{ a:1, b:2 },
{ a:11, b:12 }
]
arr2.forEach((item) => {
item.a = 10
})
console.log("循环数组元素为对象,改变原数组的对象属性值",arr2) // [{a: 10, b: 2}, {a: 10, b: 2}]
// 5. 循环过程中不支持修改索引,回调中使用return不会报错,但是无效
// 注意:不能使用break和continue跳出整个循环或当前循环的,会报错,但是结合try...catch可以实现跳出循环
// 使用try...catch...可以跳出循环
try {
let arr = [1, 2, 3, 4];
arr.forEach((item) => {
// 跳出条件
if (item === 3) {
throw new Error("LoopTerminates");
}
console.log("使用try...catch...跳出循环",item); // 1 2
});
} catch (e) {
if (e.message !== "LoopTerminates") throw e;
};
6. jQuery的each()
jQuery的$.each()
// jQuery的遍历方法通常被用来遍历DOM元素,用于数组和对象的是$.each()方法,它接受三个参数,分别指代数组索引、元素、数组本身(跟forEach相比,第1个和第2个参数正好是相反的)
// $.each()遍历数组
var arrTmp=["我","爱","中","国"]
$.each(arrTmp, function (index,value,array){
console.log(index+ ": " +value)
});
// $.each()遍历对象
var objTmp={a:'1',b:'2',c:'3'}
$.each(objTmp, function (key,value){
console.log(key+ ": " +value)
});
jQuery的$().each()
// $().each()是对页面元素的操作,此时可获取DOM元素直接操作
var resourceInputVal = $("#ResouraceModal form input");
$("#ResouraceModal .btn-default").click(function () {
// button按钮的点击事件里可以直接修改input输入框的内容
resourceInputVal.each(function (index, value) {
// 注意index在前
value.value = "";
})
})
7. map(ES6)
// 遍历每一个元素并且返回对应的元素(可以返回处理后的元素) (map 映射 一一 对应)
// 返回创建的新数组和原来旧数组的长度是一样的,使用比较广泛,但其性能还不如 forEach
// 注意:不能使用break和continue跳出整个循环或当前循环的,会报错,但是结合try...catch可以实现跳出循环
// 一、不会改变原数组
var arr = [1, 2, 3, 4, 5, 6]
var newArr = arr.map(function (item, index, arr) {
console.log("map的callback函数的第三个参数",arr)
return item * item
})
console.log("原数组1",arr) // [1, 2, 3, 4, 5, 6]
console.log("新数组1",newArr) // [1, 4, 9, 16, 25, 36]
// 二、不会改变原数组
var arr2 = [{a: 1, b: 2},{a: 11, b: 12}]
let newArr2 = arr2.map((item)=>{
return {
...item,
b:111
}
})
console.log('原数组2',arr2) // [{a: 1, b: 2},{a: 11, b: 12}]
console.log('新数组2',newArr2) // [{a: 1, b: 111},{a: 11, b: 111}]
// 三、会改变原数组元素中对象的属性值
var arr = [{a: 1, b: 2},{a: 11, b: 12}]
let newARR = arr.map((item)=>{
item.b = 111
return item
})
console.log('arr数组',arr) // [{a: 1, b: 111},{a: 11, b: 111}]
console.log('newARR',newARR) // [{a: 1, b: 111},{a: 11, b: 111}]
// 四、使用try...catch...可以跳出循环
try {
var arr = [1, 2, 3, 4];
arr.map((item) => {
//跳出条件
if (item === 3) {
throw new Error("LoopTerminates");
}
console.log("使用try...catch...跳出循环",item); // 1 2
// return item
});
} catch (e) {
if (e.message !== "LoopTerminates") throw e;
};
8. filter(ES6)
// 遍历数组,过滤出符合条件的元素并返回一个新数组
var arr = [
{ id: 1, name: '买笔', done: true },
{ id: 2, name: '买笔记本', done: true },
{ id: 3, name: '练字', done: false }
]
var newArr = arr.filter(function (item, index) {
return item.done
})
console.log("filter遍历数组过滤",newArr)
// [{ id: 1, name: '买笔', done: true },{ id: 2, name: '买笔记本', done: true }]
9. some(ES6)
// 遍历数组,只要有一个以上的元素满足条件就返回 true,否则返回 false
var arr = [
{ id: 1, name: '买笔', done: true },
{ id: 2, name: '买笔记本', done: true },
{ id: 3, name: '练字', done: false }
]
var bool = arr.some(function (item, index) {
return item.done
})
console.log("some返回值",bool) // true
10. every(ES6)
// 遍历数组,每一个元素都满足条件 则返回 true,否则返回 false
var arr = [
{ id: 1, name: '买笔', done: true },
{ id: 2, name: '买笔记本', done: true },
{ id: 3, name: '练字', done: false }
]
var bool = arr.every(function (item, index) {
return item.done
})
console.log("every返回值",bool) // false
11. find(ES6)
// 遍历数组,返回符合条件的第一个元素,如果没有符合条件的元素则返回 undefined
var arr = [1, 1, 2, 2, 3, 3, 4, 5, 6]
var num = arr.find(function (item, index) {
return item === 3
})
console.log("find返回符合条件的第一个元素",num) // 3
12. findIndex(ES6)
// 遍历数组,返回符合条件的第一个元素的索引,如果没有符合条件的元素则返回 -1
var arr = [1, 1, 2, 2, 3, 3, 4, 5, 6]
var num = arr.findIndex(function (item) {
return item === 3
})
console.log("findIndex返回符合条件的第一个元素的索引",num) // 4
13. reduce()
// reduce() 方法接收一个函数作为累加器(accumulator),数组中的每个值(从左到右)开始缩减,最终为一个值。
var total = [0,1,2,3,4].reduce((a, b)=>a + b); //10
console.log("reduce()方法total",total)
// reduce()接收一个函数,函数有四个参数,分别是:上一次的值,当前值,当前值的索引,数组
var totalVal=[0, 1, 2, 3, 4].reduce(function(previousValue, currentValue, index, array){
console.log(previousValue,"+",currentValue,"=",(previousValue+currentValue))
return previousValue + currentValue;
});
console.log("reduce()方法totalVal",totalVal)
// reduce()还有第二个参数,我们可以把这个参数作为第一次调用callback时的第一个参数,上面这个例子因为没有第二个参数,所以直接从数组的第二项开始,如果我们给了第二个参数为5,那么结果就是这样的:
// 第一次调用的previousValue的值就用传入的第二个参数代替,
var SecParam=[0, 1, 2, 3, 4].reduce(function(previousValue, currentValue, index, array){
console.log(previousValue,"+",currentValue,"=",(previousValue+currentValue))
return previousValue + currentValue;
},5);
console.log("reduce()方法SecParam",SecParam)
14. reduceRight()
// reduceRight()方法的功能和reduce()功能是一样的,不同的是reduceRight()从数组的末尾向前将数组中的数组项做累加。
// reduceRight()首次调用回调函数callbackfn时,prevValue 和 curValue 可以是两个值之一。如果调用 reduceRight() 时提供了 initialValue 参数,则 prevValue 等于 initialValue,curValue 等于数组中的最后一个值;如果没有提供 initialValue 参数,则 prevValue 等于数组最后一个值, curValue 等于数组中倒数第二个值。
var arr = [0,1,2,3,4];
var addArrValue=arr.reduceRight(function (preValue,curValue,index,array) {
console.log(preValue,"+",curValue,"=","每次调的返回值:",(preValue+curValue))
return preValue + curValue;
});
console.log("reduceRight()方法addArrValue",addArrValue) // 10
// 如果提供一个初始值initialValue为5:
var arr = [0,1,2,3,4];
var initAddValue=arr.reduceRight(function (preValue,curValue,index,array) {
console.log(preValue,"+",curValue,"=","每次调的返回值:",(preValue+curValue))
return preValue + curValue;
}, 5);
console.log("reduceRight()方法initAddValue",initAddValue) // 15
15. keys()、values()、entries()
// ES6 提供三个新的方法 —— keys()、values()和entries() —— 用于遍历数组。
// 它们都返回一个遍历器对象,可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历
for (let index of ['a', 'b'].keys()) {
console.log("keys()遍历键名",index); // 0 // 1
}
for (let elem of ['a', 'b'].values()) {
console.log("values()遍历键值",elem); // 'a' // 'b'
}
for (let [index, elem] of ['a', 'b'].entries()) {
console.log("entries()遍历键值对", index, elem); // 0 "a" // 1 "b"
}
关于跳出循环的几种方式
return =》结束循环并中断函数执行;
break =》结束循环函数继续执行;
continue =》跳过本次循环;
for 循环中的变量 i,由于ES5并没有块级作用域的存在,它在循环结束以后仍然存在于内存中,所以建议使用函数自执行的方式来避免;