关于forEach无法跳出循环

35 阅读2分钟

先来看一个问题(基础 JavaScript: 资料查找 | freeCodeCamp.org):

我们有一个对象数组,里面存储着通讯录。
`lookUpProfile` 函数已经写好了参数,需要 `name` 和属性 (`prop`) 参数。函数将会检查通讯录中是否存在一个 `firstName` 与传入的 `name` 相同的联系人。 
如果存在,那么还需要检查对应的联系人中是否存在 `prop` 属性。如果它们都存在,函数返回 prop 属性对应的值。
如果 `name` 不对应于任何联系人,然后返回字符串 `No such contact`。
如果 `prop` 属性在匹配 `name` 的联系人里不存在,返回 `No such property`const contacts = [
  {
    firstName: "Akira",
    lastName: "Laine",
    number: "0543236543",
    likes: ["Pizza", "Coding", "Brownie Points"],
  },
  {
    firstName: "Harry",
    lastName: "Potter",
    number: "0994372684",
    likes: ["Hogwarts", "Magic", "Hagrid"],
  },
  {
    firstName: "Sherlock",
    lastName: "Holmes",
    number: "0487345643",
    likes: ["Intriguing Cases", "Violin"],
  },
  {
    firstName: "Kristian",
    lastName: "Vos",
    number: "unknown",
    likes: ["JavaScript", "Gaming", "Foxes"],
  },
];

function lookUpProfile(name, prop) {
  // 只修改这一行下面的代码

  // 只修改这一行上面的代码
}

lookUpProfile("Akira", "likes");

看完这个问题后,我很快写了一段解答,我的这个lookUpProfile()是这样的:

function lookUpProfile(name, prop) {
  contacts.forEach((contact) => {
    if (contact.firstName === name) {
      if (contact[prop]) {
        return contact[prop];
      } else {
        return "No such property";
      }
    }
  });

  return "No such contact";
}

我想着用forEach循环遍历数组,如果满足条件就直接让函数返回指定数据,但运行测试发现无论输入什么参数都只会返回"No such contact",也就是只运行了最后的return "No such contact";.然后,我就猜到是forEach中使用return无法跳出循环终止函数的问题,改为 for循环后运行测试,答案正确:

function lookUpProfile(name, prop) {
  for (let i = 0; i < contacts.length; i++) {
    if (contacts[i].firstName === name) {
      if (contacts[i][prop]) {
        return contacts[i][prop];
      } else {
        return "No such property";
      }
    }
  }
  return "No such contact";
}

forEach中使用return为什么不会跳出循环?

forEach(()=>{})的使用上不难看出,它的本质是一个迭代器函数,那么也就可以理解,在forEach的回调中使用return实际上只是终止了当前这个回调(下面代码块的callback)的执行,但是这个迭代器将继续运行,开始下一次回调.来简单模拟一下forEach的原理就会很清晰了:

Array.prototype.myForEach = function (callback) {
  for (let i = 0; i < this.length; i++) {
    callback(this[i], i, this);
  }
};

那我就想终止forEach怎么办?

如果需要停止 forEach 循环,可以使用异常机制。通过抛出一个异常并在回调函数中捕获它,可以强制终止 forEach 的执行,如下所示:

const arr = [1, 2, 3, 4, 5];

try {
  arr.forEach((item) => {
    if (item === 3) {
      throw new Error("stop");
    }
    console.log(item);
  });
} catch (e) {
  if (e.message !== "stop") {
    throw e;
  } else {
    console.log(e.message);
  }
}
/*输出
1
2
stop
*/

这种方法用起来很麻烦,不到万不得已不建议使用,可以用js中提供的其他循环语句如for/for...of/while等语句来实现.

Over!!!