for、for-in、for-of、forEach的区别
总结
for..of适用遍历数/数组对象/字符串/map/set等拥有迭代器对象的集合.但是不能遍历对象,因为没有迭代器对象.与forEach()不同的是,它可以正确响应break、continue和return语句。
for-of循环不支持普通对象,但如果你想迭代一个对象的属性,你可以用for-in循环(这也是它的本职工作)或内建的Object.keys()方法。
for in遍历的是数组的索引(即键名),而for of遍历的是数组元素值。for of遍历的只是数组内的元素,而不包括数组的原型属性method和索引name。
forEach(value,index,Array)不能同时遍历多个集合,在遍历的时候无法修改和删除集合数据,方法不能使用break,continue语句跳出循环,或者使用return从函数体返回,对于空数组不会执行回调函数。
forEach与for当循环遍历为空的数组时,forEach会跳过为空的值,for不会跳过为空的数据,会直接显示undefined。两者都能识别出NULL。
forEach()
被调用时,不会改变原数组,也就是调用它的数组(尽管 callback
函数在被调用时可能会改变原数组)。
具体区别如下:
FOR
var arrList = [1,2,3,4,5,6]
for(var i = 0;i < arrList.length ; i++){
console.log(arrList[i]);
}
FOR-IN
语法
for (variable in object)
statement
-
variable
在每次迭代时,variable会被赋值为不同的属性名。
-
object
非Symbol类型的可枚举属性被迭代的对象。
描述
for...in
循环只遍历可枚举属性(包括它的原型链上的可枚举属性)。像 Array
和 Object
使用内置构造函数所创建的对象都会继承自Object.prototype
和String.prototype
的不可枚举属性,例如 String
的 indexOf()
方法或 Object
的toString()
方法。循环将遍历对象本身的所有可枚举属性,以及对象从其构造函数原型中继承的属性(更接近原型链中对象的属性覆盖原型属性)。
举例
/*
FOR..IN语句遍历数组
*/
Array.prototype.method=function(){
console.log(this.length);
}
var myArray=[1,2,4]
myArray.name="数组"
for (var index in myArray) {
console.log(myArray[index]);
}
/*
输出结果顺序不唯一。输出的结果包含如下:
1
2
4
数组
ƒ (){
console.log(this.length);
}
*/
/*
FOR..IN语句遍历对象
*/
Object.prototype.method=function(){
console.log(this);
}
var myObject={
a:1,
b:2,
c:3
}
for (var key in myObject) {
console.log(key);
}
/*
输出结果顺序不唯一。输出的结果包含如下:
a
b
c
method
1- for in 可以遍历到myObject的原型方法method,如果不想遍历原型方法和属性的话,可以在循环内部判断一 下,hasOwnPropery方法可以判断某属性是否是该对象的实例属性.
2- 通过ES5的Object.keys(myObject)获取对象的实例属性组成的数组,不包括原型方法和属性.
*/
for (var key in myObject) {
if(myObject.hasOwnProperty(key)){
console.log(key);
}
}
/*
输出结果顺序不唯一。输出的结果包含如下:
a
b
c
*/
注意事项
-
FOR..IN语句遍历的是索引index(键名)为字符串型数字,不能直接进行几何运算。
-
使用for in会遍历数组所有的可枚举属性,包括原型。例如上述案例的原型方法method和name属性,所以for in更适合遍历对象,不要使用for in遍历数组。
删除,添加或者修改属性
for...in
循环以任意序迭代一个对象的属性(请参阅delete
运算符,了解为什么不能依赖于迭代的表面有序性,至少在跨浏览器设置中)。如果一个属性在一次迭代中被修改,在稍后被访问,其在循环中的值是其在稍后时间的值。一个在被访问之前已经被删除的属性将不会在之后被访问。在迭代进行时被添加到对象的属性,可能在之后的迭代被访问,也可能被忽略。
通常,在迭代过程中最好不要在对象上进行添加、修改或者删除属性的操作,除非是对当前正在被访问的属性。这里并不保证是否一个被添加的属性在迭代过程中会被访问到,不保证一个修改后的属性(除非是正在被访问的)会在修改前或者修改后被访问,不保证一个被删除的属性将会在它被删除之前被访问。
FOR-OF
for...of
语句在可迭代对象(包括 Array
,Map
,Set
,String
,TypedArray
,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句。不建议遍历原有的原生对象。
语法
for (variable of iterable) {
//statements
}
-
variable
在每次迭代中,将不同属性的值分配给变量。
-
iterable
被迭代枚举其属性的对象。
示例
/*
FOR..OF语句遍历数组
*/
var myArray=[1,2,4,5,6,7,8]
for (var index of myArray) {
console.log(index);
}
/*
输出结果顺序唯一。输出的结果如下:
1
2
4
5
6
7
8
*/
FOREACH
语法
arr.forEach(callback(currentValue [, index [, array]])[, thisArg]);
参数
-
callback
为数组中每个元素执行的函数,该函数接收三个参数:
currentValue
数组中正在处理的当前元素。index
可选数组中正在处理的当前元素的索引。array
可选forEach()
方法正在操作的数组。 -
thisArg
可选可选参数。当执行回调函数
callback
时,用作this
的值。
返回值
undefined
描述
forEach()
方法按升序为数组中含有效值的每一项执行一次 callback
函数,那些已删除或者未初始化的项将被跳过(例如在稀疏数组上)。
举例
/*
数组中未定义或者undefined数据的情况。
*/
const arraySparse = [1,3,,7];
let forCallbackRuns = 0;
let forEachCallbackRuns = 0;
arraySparse.forEach(function(element){
console.log(element);
forEachCallbackRuns++;
});
console.log("forEachCallbackRuns: ", forEachCallbackRuns);
/*
1
3
7
numCallbackRuns: 3
*/
for(var i = 0; i < arraySparse.length ; i++){
console.log(arraySparse[i]);
forCallbackRuns++;
}
console.log("forCallbackRuns: ", forCallbackRuns);
/*
1
3
undefined
7
forCallbackRuns: 4
*/
/*
数组中含有null数据的情况。
*/
const arraySparse = [1,3,null,7];
let forCallbackRuns = 0;
let forEachCallbackRuns = 0;
arraySparse.forEach(function(element){
console.log(element);
forEachCallbackRuns++;
});
console.log("forEachCallbackRuns: ", forEachCallbackRuns);
/*
1
3
null
7
forEachCallbackRuns: 4
*/
for(var i = 0; i < arraySparse.length ; i++){
console.log(arraySparse[i]);
forCallbackRuns++;
}
console.log("forCallbackRuns: ", forCallbackRuns);
/*
1
3
null
7
forCallbackRuns: 4
*/
注意
除了抛出异常以外,没有办法中止或跳出 forEach()
循环。如果你需要中止或跳出循环,forEach()
方法不是应当使用的工具。
若你需要提前终止循环,你可以使用:
- 一个简单的 for 循环
- for...of / for...in 循环
Array.prototype.every()
Array.prototype.some()
Array.prototype.find()
Array.prototype.findIndex()
这些数组方法则可以对数组元素判断,以便确定是否需要继续遍历:
译者注:只要条件允许,也可以使用 filter()
提前过滤出需要遍历的部分,再用 forEach()
处理。
参考资料链接
作者:Haiya_32ef 链接:https://www.jianshu.com/p/c43f418d6bf0
作者:weixin_34128411 链接:https://blog.csdn.net/weixin_34128411/article/details/94279246