如何在JavaScript中停止各种循环(for, forEach, map, for...in等):实用技巧与方法汇总
话不多说,直接看代码
const arr = [1, 2, 3, 4, 5, 6];
for
-
break
结束循环
for (let i = 0; i < arr.length; i++) {
if (i === 3) {
break;
}
console.log(arr[i]); // 1 2 3
}
-
continue
跳过当前循环
for (let i = 0; i < arr.length; i++) {
if (i === 3) {
continue;
}
console.log(arr[i]); // 1 2 3 5 6
}
forEach
并没有优雅的方法结束forEach循环,有的小伙伴可能会说,return不行咩。
arr.forEach((item) => {
if (item === 3) {
// 此处return和return true,return false都一样
return;
}
console.log(item); // 1 2 4 5 6
});
这里我也有同样的疑惑,forEach
实质上是Array.prototype
上的一个方法,既然是方法,那为什么retrun不能直接终止呢?????
带着这样的疑惑,在网上找了一下,实在找不到v8的源码,借用mozilla的forEach源码????
/* ES5 15.4.4.18. */
function ArrayForEach(callbackfn /*, thisArg*/) {
var O = ToObject(this);
var len = ToLength(O.length);
if (arguments.length === 0)
ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "Array.prototype.forEach");
if (!IsCallable(callbackfn))
ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
var T = arguments.length > 1 ? arguments[1] : void 0;
for (var k = 0; k < len; k++) {
if (k in O) {
callContentFunction(callbackfn, T, O[k], k, O);
}
}
return void 0;
}
核心代码我们可以看到原来是for循环对每个元素都执行了一次fn,那这就不难理解为什么return终止不了forEach循环了。
因为我们传入的方法,这里为if(item === 3) return
,是在for循环内部执行的,return只结束了当item === 3
所执行的fn,即return
,但是for循环还是会继续执行。所以上面的代码log的结果是1 2 4 5 6
tips: 这里我们也可以看出来forEach的返回值是void 0
,即undefined
,那么下次当我们再听到forEach
和map
的区别是forEach没有返回值时,我们就可以很装x的说:forEach也有返回值,只不过是undefined
,没有意义而已????
知道了怎么回事,我们自己撸一个forEach
arr.forEach(callback(currentValue [, index [, array]])[, thisArg]) - MDN
Array.prototype.forEach = function (fn, thisArg) {
if (typeof fn !== "function") {
throw new TypeError(`${fn} is not a function`);
}
for (var i = 0; i < this.length; i++) {
fn.apply(thisArg, [this[i], i, this]);
}
};
那有的小伙伴可能要说了,我就要结束forEach
循环,那当然也不是没有办法,抛出错误就行了
try {
arr.forEach((item) => {
if (item === 3) {
throw new Error("exit foreach");
}
console.log(item); // 1 2
});
} catch (e) {}
这里可以看一下MDN上对forEach的说明:
map
map与forEach类似,唯一的区别就是
map
方法会给原数组中的每个元素都按顺序调用一次callback
函数。callback
每次执行后的返回值(包括undefined
)组合起来形成一个新数组。
这里再贴一下MDN对map的使用说明
因为map生成一个新数组,当你不打算使用返回的新数组却使用map是违背设计初衷的,请用forEach或者for-of替代。你不该使用map:
A)你不打算使用返回的新数组,
B)你没有从回调函数中返回值。
Array.prototype.map()-MDN
for...in
break
结束循环
const person = {
name: "ming",
age: 18,
1: 1,
job: "student",
};
for (const key in person) {
if (key === "age") {
break;
}
console.log(`${key}: ${person[key]}`);
// 1: 1
// name: ming
}
continue
跳过当前循环
const person = {
name: "ming",
age: 18,
1: 1,
job: "student",
};
for (const key in person) {
if (key === "age") {
continue;
}
console.log(`${key}: ${person[key]}`);
// 1: 1
// name: ming
// job: student
}
这里可以看到,key
为1
的元素比key
为name
的元素先打印了出来,
for...in
语句以任意顺序遍历一个对象的除Symbol以外的可枚举属性。
os: 本来for...in
就是为了遍历对象的,然而对象本来就是无序的 ????️
顺手撸一个深拷贝
function deepClone(target) {
if (typeof target !== "object" || target === null) return target;
let result = Array.isArray(target) ? [] : {};
for (const key in target) {
if (target.hasOwnProperty(key)) {
const element = target[key];
if (typeof element === "object") {
result[key] = deepClone(element);
} else {
result[key] = element;
}
}
}
return result;
}
注:当for...in
用来遍历数组时,遍历的结果为当前元素索引值的字符串形式
for...of
break
结束循环
for (const val of arr) {
if (val === 3) {
break;
}
console.log(val); // 1 2
}
continue
跳过当前循环
for (const val of arr) {
if (val === 3) {
continue;
}
console.log(val); // 1 2 4 5 6
}
我们都知道for...of
只能用来遍历那些内置iterator
(Array, Atring, ArrayLike, Set, Map...)或者实现了@@iterator
方法的数据类型,而普通的Object
并没有内置iterator
这里简单说一下用for...of
遍历普通对象的两种方法:
- 转成array
for (const [key, value] of Object.entries(obj)) {
console.log(`${key}: ${value}`);
}
- 实现iterator
person[Symbol.iterator] = function () {
let nextIndex = 0;
const arr = Object.entries(this);
return {
next: function () {
return nextIndex < arr.length
? {
value: arr[nextIndex++],
done: false,
}
: {
done: true,
};
},
};
};