Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

你不知道的循环中断 #1

Open
maicFir opened this issue Aug 11, 2022 · 0 comments
Open

你不知道的循环中断 #1

maicFir opened this issue Aug 11, 2022 · 0 comments

Comments

@maicFir
Copy link
Owner

maicFir commented Aug 11, 2022

你知道 JS 中断循环有哪些吗?除了 for 循环的 break,还有哪些可以中断循环?接下来笔者以实际业务例子,分享几种能中断循环的方案,希望你在实际业务中能用得上。

forEach

在实际业务中你可能会写以下的业务代码,举个栗子,在一个循环表单域中,你需要内容为空,就禁止提交

::: details code

const shopList = [
  { title: 'Apple', price: 10 },
  { title: 'banana', price: '' },
  { title: 'orange', price: 5 }
];

:::

以上是一组数组源,于是你的思路可能会这样

::: details code

const hasPriceEmpty = (arr) => {
  bool = false; // 默认都不是空
  arr.forEach((v) => {
    if (v.price === '') {
      bool = true;
      break;
    }
    console.log(v);
  });
  return bool;
};
const handleSubmit = () => {
  if (hasPriceEmpty(shopList)) {
    return;
  }
  // 下面的继续业务操作
  console.log('go on...');
};
handleSubmit();

:::

运行测试命令node 1.js,报错了!

图片

于是你把 break 改成 return

::: details code

const hasPriceEmpty = (arr) => {
  bool = false; // 默认都不是空
  arr.forEach((v) => {
    if (v.price === '') {
      bool = true;
      return;
    }
    console.log(v);
  });
  return bool;
};
...

:::

图片

你会发现并没有打印go on...,确实是hasPriceEmpty这个方法已经达到了自己的业务要求,但是打印出了第一组和第三组数据。

于是mdn上关于 forEach 有这么一段话,Note: There is no way to stop or break a forEach() loop other than by throwing an exception. If you need such behavior, the forEach() method is the wrong tool.

大概意思就是除了抛出异常,break 无法中断循环,如果你想有中断行为,forEach不是一个好办法。

于是你想中断循环,你改了下代码
::: details code

const shopList = [
  { title: 'Apple', price: 10 },
  { title: 'banana', price: '' },
  { title: 'orange', price: 5 }
];
const hasPriceEmpty = (arr) => {
  bool = false; // 默认都不是空
  arr.forEach((v) => {
    if (v.price === '') {
      bool = true;
      throw new Error('给我中断循环吧');
    }
    try {
      console.log(v);
    } catch (e) {
      console.log(e);
      console.log(v);
    }
  });
  return bool;
};
const handleSubmit = () => {
  if (hasPriceEmpty(shopList)) {
    return;
  }
  // 下面的继续业务操作
  console.log('go on...');
};
handleSubmit();

:::

对不起,页面只打印了第一组数据,且页面抛出了异常
图片我确实做到了中断 forEach 循环异常了,但是这个错误作为一个强迫症患者,我是不能接受的(throw 抛出的异常,记得 try catch 中捕获)。

for 循环 break 中断

这是笔者认为大部分人都能想到的办法
::: details code

const shopList = [
  { title: 'Apple', price: 10 },
  { title: 'banana', price: '' },
  { title: 'orange', price: 5 }
];
const hasPriceEmpty = (arr) => {
  let bool = false; // 默认都不是空
  for (let i = 0, len = arr.length; i < len; i++) {
    if (arr[i].price === '') {
      bool = true;
      break;
    }
    console.log(arr[i]);
  }
  return bool;
};
const handleSubmit = () => {
  if (hasPriceEmpty(shopList)) {
    return;
  }
  // 下面的继续业务操作
  console.log('go on...');
};
handleSubmit();

:::

结果很令人欢喜,完美

图片通常这种方式用得最多,但是作为老司机,你还需要掌握多一点其他方法。于是中断循环还有...

while 循环中断

::: details code

const shopList = [
  { title: 'Apple', price: 10 },
  { title: 'banana', price: '' },
  { title: 'orange', price: 5 }
];
const hasPriceEmpty = (arr) => {
  let bool = false; // 默认都不是空
  let index = 0;
  while (index < arr.length) {
    index++;
    console.log(arr[index], '000');
    if (arr[index].price === '') {
      bool = true;
      break;
    }
    console.log(arr[index], '111');
  }
  return bool;
};
const handleSubmit = () => {
  if (hasPriceEmpty(shopList)) {
    return;
  }
  // 下面的继续业务操作
  console.log('go on...');
};
handleSubmit();

:::

结果喜大普奔,依然可以,测试结果如下

图片

这已经达到我们想要的效果了。

利用 iterable 迭代器,for...of 中断循环

这里iterable是指具有该特性的迭代器,比如ArrayMapSet

Array

::: details code

const shopList = [
  { title: 'Apple', price: 10 },
  { title: 'banana', price: '' },
  { title: 'orange', price: 5 }
];
const hasPriceEmpty = (arr) => {
  let bool = false; // 默认都不是空
  for (let item of arr) {
    if (item.price === '') {
      bool = true;
      break;
    }
    console.log(item, '111');
  }
  return bool;
};
const handleSubmit = () => {
  if (hasPriceEmpty(shopList)) {
    return;
  }
  // 下面的继续业务操作
  console.log('go on...');
};
handleSubmit();

:::

于是测试结果依旧 ok

图片

为什么数组可以用for..of循环,你可以在控制台打印注意到

图片

原来默认申明的[]原型链上有一个这样的iterator的迭代器,所以你可以利用 iterator 的特性,用for...of中断循环

Map

::: details code

const shopList = [
  { title: 'Apple', price: 10 },
  { title: 'banana', price: '' },
  { title: 'orange', price: 5 }
];
const hasPriceEmpty = (arr) => {
  let bool = false; // 默认都不是空
  const map = new Map();
  // 将数据设置到Map中
  arr.forEach((item) => {
    map.set(item.title, item);
  });
  for (let s of map) {
    console.log(s, '000');
    if (s[1].price === '') {
      bool = true;
      break;
    }
    console.log(s, '111');
  }
  return bool;
};
const handleSubmit = () => {
  if (hasPriceEmpty(shopList)) {
    return;
  }
  // 下面的继续业务操作
  console.log('go on...');
};
handleSubmit();

:::

结果如下图片

以上代码也等价于
::: details code

const sourceData = [
  { title: 'Apple', price: 10 },
  { title: 'banana', price: '' },
  { title: 'orange', price: 5 }
];
const shopList = new Map([sourceData]);

const hasPriceEmpty = (arr) => {
  let bool = false; // 默认都不是空
  for (let s of arr) {
    console.log(s, '000');
    if (s[1].price === '') {
      bool = true;
      break;
    }
    console.log(s, '111');
  }
  return bool;
};

const handleSubmit = () => {
  if (hasPriceEmpty(shopList)) {
    return;
  }
  // 下面的继续业务操作
  console.log('go on...');
};
handleSubmit();

:::
验证结果如下:

图片

利用 Map 有iterable的可迭代性,通过for...of中断循环,具体可以在控制台下验证

图片

Set

同样是一个栗子举证
::: details code

const shopList = [
  { title: 'Apple', price: 10 },
  { title: 'banana', price: '' },
  { title: 'orange', price: 5 }
];
const hasPriceEmpty = (arr) => {
  let bool = false; // 默认都不是空
  const setArr = new Set([...shopList]);
  for (let s of setArr) {
    console.log(s, '000');
    if (s.price === '') {
      bool = true;
      break;
    }
    console.log(s, '111');
  }
  return bool;
};

const handleSubmit = () => {
  if (hasPriceEmpty(shopList)) {
    return;
  }
  // 下面的继续业务操作
  console.log('go on...');
};
handleSubmit();

:::

输出验证结果:

图片

SetMap一样,可以在控制台验证一下iterable属性,我就不验证了,呜呜。

总结

1.forEach的中断循环可以抛异常来达到目的,但是不适合此业务场景

2.for 循环通用大法,break可以终止循环

3.while循环,break也可以终止循环

4.iterable特征的可迭代器,for...ofbreak中断循环,并且最重要的一点是在 break 后,当前索引条件不会继续执行,也就是 for...of 中,执行 break 后,后面语句都不会执行。

5.本文示例code-example

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant