面试:你能终止JS的forEach循环吗?

60 阅读3分钟

面试:你能终止JS的forEach循环吗?

3种方法终止forEach循环

Photo by 傅甬 华

面试官: 你能终止JS的forEach循环吗?  这个问题我曾经在我的一次面试中被问过, 我的答案是  “不,我不能”

很不幸, 我的回答让面试官结束了这场面试。

这个结果很沮丧, 我问面试官, “为什么?真的有可能终止JS的forEach循环吗?”

在面试官回答之前,我解释了我的理解,为什么不能直接停止JS的forEach循环。

我的回答是正确的吗?

朋友们,你们觉得下面的代码会输出什么数字?

是输出一个呢?还是多个?

const array = [ -3, -2, -1, 0, 1, 2, 3 ]

array.forEach((it) => {
  if (it >= 0) {
    console.log(it)
    return // 或者 break
  }
})

是的,它会输出 ‘0’, ‘1’, ‘2’, ‘3’.

没错! 我把代码展示给面试官看, 但是他仍然坚定有办法终止JS的forEach循环。

Photo by Christian Erfurt 

为什么?

为了说服他, 我不得不手动模拟实现一个 forEach 循环。

Array.prototype.forEach2 = function (callback, thisCtx) {
  if (typeof callback !== 'function') {
    throw `${callback} is not a function`
  }

  const length = this.length
  console.log('length>>>', length)
  let i = 0

  while (i < length) {
    if (this.hasOwnProperty(i)) {
      // 关键在这里,每一个循环语句都会在这里被直接执行
      console.log('i>>>', i, this[i])
      callback.call(thisCtx, this[i], i, this)
    }
    i++
  }
}

当我们用forEach去遍历一个数组的时候, 回调函数会立即执行数组里的每一个元素, 没有任何办法去打断执行。 为什么"break"或者"return"语句在这里就不生效呢?

很简单的例子,在下面的代码里,即使 ‘func1’ 遇到 'return' 语句, ‘2’ 也会被输出到控制台。

const func1 = () => {
  console.log(1)
  return
}

const func2 = () => {
  func1()
  console.log(2)
}

func2()

所以对于上面模拟的forEach循环来说,while内部执行的回调函数,即使回调函数里有"return"语句,也只是影响回调函数的执行,不会影响代码继续往下执行。

3种方法终止forEach循环

你很棒, 但是我要告诉你我们至少有3种方法终止forEach循环.

1. 抛出一个错误

当找到第一个大于等于的数字,这段代码就不会继续执行。所以只会打印输出0

const array = [ -3, -2, -1, 0, 1, 2, 3 ]

try {
  array.forEach((it) => {
    if (it >= 0) {
      console.log(it)
      throw Error(`We've found the target element.`)
    }
  })
} catch (err) {
  
}

卧槽,还真的终止了···

2. 设置数组长度为0

不要惊讶,这是面试官告诉我的方法。

我们也可以通过设置数组长度为0终止forEach。正如你所知的那样,数组长度为0,forEach不会执行任何回调。

const array = [ -3, -2, -1, 0, 1, 2, 3 ]

array.forEach((it) => {
  if (it >= 0) {
    console.log(it)
    array.length = 0
  }
})

3. 用splice方法去除数组元素

类似于方法2,你可以删除目标值后面的所有元素,然后forEach将会自动停止。

const array = [ -3, -2, -1, 0, 1, 2, 3 ]

array.forEach((it, i) => {
  if (it >= 0) {
    console.log(it)
    array.splice(i + 1, array.length - i)
  }
})

注意

虽然用改变数组长度的方法,回调函数“貌似”不再执行,但是其实forEach内部的while循环还会继续执行,因为while循环次数在一开始就确定下来了,只是改变了数组,导致或者不到元素了。

建议:请使用for循环或者some方法

我对面试官说, “可能你是对的,虽然你成功终止了forEach循环, 但是我想你的老板会炒了你的鱿鱼,因为这真的是一段很糟糕的代码。我不喜欢写这样的代码,同事们会恨死我”

我们可以用 for or some 方法来解决。

1. for

const array = [ -3, -2, -1, 0, 1, 2, 3 ]

for (let i = 0, len = array.length; i < len; i++) {
  if (array[i] >= 0) {
    console.log(array[i])
    break
  }
}

2. some

const array = [ -3, -2, -1, 0, 1, 2, 3 ]

array.some((it, i) => {
  if (it >= 0) {
    console.log(it)
    return true
  }
})

结束语

尽管面试官以这个问题结束了面试,但是我很高兴,我不用加入这个公司,不用写这一坨屎一样的代码。真的是依托答辩。

引用链接:medium.com/javascript-…