【Javascript】关于如何使用循环,看这篇就够了

每种编程语言都有循环。循环执行某个操作(也就是一段工作)多次,通常是对数组或列表中的每一项进行操作,或者重复执行直到满足某个条件为止。

JavaScript 尤其提供了多种不同类型的循环。我自己甚至都没有用过所有这些类型,出于好奇心,我想做个高层次的概览。结果发现,至少有几种循环类型没被广泛采用是有充分理由的。

那么,咱们就先用 for 循环开始,花点 while 时间来探索各种不同的循环类型,看看我们能用 each 做些什么,以及为什么你可能会选择一种而非另一种。读到最后你会觉得我这里的小文字游戏绝对逗乐。

whiledo...while 循环

首先登场的是 while 循环。这是最基础的循环类型,在很多情况下也是最容易阅读和最快的。它通常用于做某件事直到满足某个条件。这也是最容易制造无限循环或永不停止的循环的方式。还有 do...while 语句。实际上,唯一的区别在于条件是在每次迭代的末尾而不是开头检查。

javascript 复制代码
// 从数组中移除第一个元素并打印出来,直到数组为空
let queue1 = ["a", "b", "c"];

while (queue1.length) {
  let item = queue1.shift();

  console.log(item);
}

// 上面的相同逻辑,但也会打印数组为空时的信息
let queue2 = [];

do {
  let item = queue2.shift() ?? "empty";

  console.log(item);
} while (queue2.length);

for 循环

接下来是 for 循环。如果你需要重复执行某个操作一定次数,比如十次,那这就是首选。这种循环对编程新手来说可能有点吓人,但是把相同的循环用 while 风格重写一下,可以帮助理解语法,也更容易记住。

javascript 复制代码
// 打印数字 1 到 5
for (let i = 1; i <= 5; i++) {
  console.log(i);
}

// 同样的事情,但是用 `while` 循环实现
let i = 1; // `for` 循环的第一部分

// 第二部分
while (i <= 5) {
  console.log(i);

  i++; // 第三部分
}

("end");

for...offor await...of 循环

for...of 循环是最简单的方式来遍历数组。

javascript 复制代码
let myList = ["a", "b", "c"];

for (let item of myList) {
  console.log(item);
}

不过它们不仅仅限于数组。技术上讲,它们可以遍历实现了所谓的 可迭代协议 的任何东西。有一些内置类型实现了该协议:数组、映射、集合和字符串,这些是最常见的,但你也可以在自己的代码中实现该协议。做法是在任意对象中添加一个 [Symbol.iterator] 方法,而这个方法应该返回一个迭代器。这有点让人困惑,但大概意思是可迭代的对象是一种特殊的方法,返回迭代器;可以说是迭代器的工厂方法。一种特殊的函数叫做 生成器,它既能返回可迭代对象也能返回迭代器。

javascript 复制代码
let myList = {
  *[Symbol.iterator]() {
    yield "a";
    yield "b";
    yield "c";
  },
};

for (let item of myList) {
  console.log(item);
}

上面提到的所有东西都有异步版本:异步可迭代对象、异步迭代器和异步生成器。你可以用 for await...of 来使用异步可迭代对象。

javascript 复制代码
async function delay(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

// 这次我们不是创建一个可迭代对象,而是生成器
async function* aNumberAMinute() {
  let i = 0;

  while (true) {
    // an infinite loop
    yield i++;

    // pause a minute
    await delay(60_000);
  }
}

// 它是一个生成器,所以我们需要自己调用它
for await (let i of aNumberAMinute()) {
  console.log(i);

  // stop after one hour
  if (i >= 59) {
    break;
  }
}

有一点不太明显的是,你可以用非异步可迭代对象与 for await...of 一起使用,而且完全没问题。然而反过来就不行;你不能用异步可迭代对象与 for...of 一起使用。

forEachmap "循环"

虽然这些严格意义上不算循环,但你可以用它们来遍历列表。

说说 forEach 方法吧。历史上它的速度远不如 for 循环。我认为现在有些情况下这种情况可能已经改变,但如果性能是个问题,我还是会避免使用它。有了 for...of 之后,我不确定还有什么理由要用它。也许唯一的原因是你已经有了一个可以直接作为回调使用的函数,但你也可以很容易地在 for...of 的主体里调用同一个函数。

forEach 还接收每个元素的索引,所以这可能是你需要的功能。最终是否使用它,可能会取决于你正在处理的其他代码是否也在使用它,但如果是新写的代码我个人会尽量避免使用它。

javascript 复制代码
let myList = ["a", "b", "c"];

for (let item of myList) {
	console.log(item);
}

// 如果我需要索引,或许可以用 `forEach`
["a", "b", "c"].forEach((item, index) => {
  console.log(`${index}: ${item}`);
});

同时,map 实质上是将一个数组转换为另一个数组。它与 forEach 一样有性能影响,但它比替代方案更好读一点。当然这是主观判断,就像 forEach 一样,你会希望遵循你的其他代码的做法。在 React 和受 React 启发的库中,你会经常看到它作为遍历数组并在 JSX 中输出项目列表的主要方式。

javascript 复制代码
function MyList({items}) {
  return (
    <ul>
      {items.map((item) => {
        return <li>{item}</li>;
      })}
    </ul>
  );
}

for...in 循环

说到 JavaScript 中的循环,如果不提 for...in 语句,这个列表就不完整了,因为它可以遍历对象的字段。它还会遍历通过对象原型链继承的字段,这正是我一直避免使用它的原因。

话虽如此,如果你有一个对象字面量,那么 for...in 可能是遍历该对象键的有效方式。值得一提的是,如果你编写 JavaScript 已经有一段时间了,你可能还记得键的顺序曾经在不同浏览器之间不一致,但现在顺序是一致的。任何可能作为数组索引的键(即正整数)都会按升序排列在前面,然后是按声明顺序排列的其他键。

javascript 复制代码
let myObject = {
  a: 1,
  b: 2,
  c: 3,
};

for (let k in myObject) {
  console.log(myObject[k]);
}

结语

循环是许多程序员每天都在使用的东西,尽管我们可能会习以为常,不太去思考它们。

但当你退一步来看我们在 JavaScript 中遍历事物的各种方式时,你会发现其实有很多种方法可以做到这一点。不仅如此,它们之间存在显著------即使细微------的区别,这些差异确实会影响你的编写方式。

相关推荐
cwj&xyp20 分钟前
Python(二)str、list、tuple、dict、set
前端·python·算法
dlnu201525062222 分钟前
ssr实现方案
前端·javascript·ssr
古木201926 分钟前
前端面试宝典
前端·面试·职场和发展
Kisorge1 小时前
【C语言】指针数组、数组指针、函数指针、指针函数、函数指针数组、回调函数
c语言·开发语言
轻口味2 小时前
命名空间与模块化概述
开发语言·前端·javascript
前端小小王2 小时前
React Hooks
前端·javascript·react.js
迷途小码农零零发3 小时前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
晓纪同学3 小时前
QT-简单视觉框架代码
开发语言·qt
威桑3 小时前
Qt SizePolicy详解:minimum 与 minimumExpanding 的区别
开发语言·qt·扩张策略