一 ❀ 引
先来看一段代码:
let arr = [1, 2];
arr.forEach((item, index) => {
arr.splice(index, 1);
console.log(11111); //输出几次?
});
console.log(arr) //?
let arr1 = [1, 2];
for (var i = 0; i < arr1.length; i++) {
arr1.splice(i, 1);
console.log(11111); //输出几次?
}
console.log(arr1) //?
请问,这段代码执行完毕后arr输出为多少?循环体内的console操作会执行几次?
二 ❀ forEach参数
forEach有的也叫增强for循环,forEach其实是for循环的一个特殊简化版。
与for循环一样,forEach也属于完整遍历数组的方法,并会对数组每项元素执行提供的回调函数,一个完整的forEach应该是这样,如下:
arr.forEach(function(self,index,arr){
},this);
self:数组当前遍历的元素,默认从左往右依次获取数组元素。
index:数组当前元素的索引,第一个元素索引为0,依次类推。
arr:当前遍历的数组。
this:回调函数中this指向。
我们来看个简单的forEach例子,加强对于这四个参数的印象:
let arr1 = [1, 2, 3, 4];
let obj = {
a: 1
};
arr1.forEach(function (self, index, arr) {
console.log(`当前元素'self'为${self}索引'index'为${index},属于数组'arr'${arr}`);
console.log(this);
}, obj)
可以看到,arr参数其实就是我们正在遍历的数组,而回调函数中的this指向我们提供的obj。
但若是forEach中使用了箭头函数()=>{}
,则this指向的不在是当前的回调函数,而是window。
let arr1 = [1, 2, 3, 4];
let obj = {
a: 1
};
arr1.forEach((self, index, arr) => {
console.log(`当前元素'self'为${self}索引'index'为${index},属于数组'arr'${arr}`);
console.log(this);
console.log(obj);
}, obj)
forEach虽然是for循环的简化版本,但是并不是说foreach就比for更好用,forEach适用于循环次数未知,或者计算循环次数比较麻烦情况下使用效率更高,但是更为复杂的一些循环还是需要用到for循环效率更高。
三 ❀ forEach使用
1.forEach不支持break
大家都知道,在使用for循环时可以使用break跳出循环,比如我希望找到数组中符合条件的第一个元素就跳出循环,这对于优化数组遍历是非常棒的。很遗憾,forEach并不支持break操作,使用break会导致报错。
let arr = [1, 2, 3, 4],
i = 0,
length = arr.length;
for (; i < length; i++) {
console.log(arr[i]); //1,2
if (arr[i] === 2) {
break;
};
};
arr.forEach((self,index) => {
console.log(self);
if (self === 2) {
break; //报错
};
});
那forEach能不能跳出循环呢?可以。
2.forEach结合try…catch可以跳出循环
try {
var arr = [1, 2, 3, 4];
arr.forEach(function (item, index) {
//跳出条件
if (item === 3) {
throw new Error("LoopTerminates");
}
//do something
console.log(item);
});
} catch (e) {
if (e.message !== "LoopTerminates") throw e;
};
3.forEach中使用return无效
首先需要确定的,直接在for循环中使用return会报错(函数中使用for可以return)
,forEach中使用return不会报错,但rerutn并不会生效
。
let arr = [1, 2, 3, 4];
function forFn(array, num) {
for (var i = 0; i < array.length; i++) {
if (array[i] == num) {
return i
}
}
};
function forEachFn(array, num) {
array.forEach((self, index) => {
if (self === num) {
return index;
};
});
};
let index1 = forFn(arr, 2);
let index2 = forEachFn(arr, 2);
console.log(index1) // 1
console.log(index2) // undefined
上述代码forEachFn
方法 想要找到数字2在数组中的索引,但return并不会起到终止代码运行并返回值的作用。
当然如果我们真的要用return返回某个值,那就只能将return操作放在函数中,而不是在forEach循环中,像这样:
let arr = [1, 2, 3, 4];
function forEachFnNew(array, num) {
let i = "";
array.forEach((self, index) => {
if (self === num) {
i = index;
};
});
return i;
};
let index3 = forEachFnNew(arr, 2);
console.log(index3) // 1
4.forEach删除自身元素index不会被重置
还记得文章开头的问题吗,那段代码其实只会执行一次,数组也不会被删除干净,这是因为forEach在遍历跑完回调函数后,会隐性让index自增。
5.函数中for循环中break和return区别是什么?
1.break:指的是跳出for循环本身,不再进行之后的循环,但可以执行for循环之外的语句。
function test() {
for (var i = 1; i <= 5; i++) {
if (i === 4) {
break;
}
console.log(i); // 分别输出 1,2, 3
}
console.log("end"); // 输出end
}
2.return:指的是跳出for循环,且不执行for循环之外的语句,直接跳出当前函数,返回return后的值。
function test() {
for (var i = 1; i <= 5; i++) {
if (i === 4) {
return false;
}
console.log(i); // 分别输出 1,2, 3
}
console.log("end"); // 未执行
}
3.continue(补充):语句和break语句差不多。不同的是,它不是退出整个循环,而是跳出当前循环,进行下一轮循环(i++)
function test() {
for (var i = 1; i <= 5; i++) {
if (i === 4) {
continue;
}
console.log(i); // 分别输出 1,2, 3 5
}
console.log("end"); // 输出end
}
四 ❀ for与forEach的区别
- for循环可以使用break跳出循环,但forEach不能。
- for循环可以控制循环起点(i初始化的数字决定循环的起点),forEach只能默认从索引0开始。
- for循环过程中支持修改索引(修改 i),但forEach做不到(底层控制index自增,我们无法左右它)。
五 ❀扩展 forEach与map的区别
相同点:
- 都是循环遍历数组中的每一项;
- 每次执行匿名函数都支持三个参数,参数分别为item(当前每一项),index(素引值),arr(原数组)。
- 匿名函数中的this都是指向window。
- 只能遍历数组。
不同点:
- map()会分配内存空间存储新数组并返回,forEach()不会返回数据;
- forEach()允许callback更改原始数组的元素。map()返回新的数组。