47

不再写 break 和 continue 了

 5 years ago
source link: https://juejin.im/post/5d08a565e51d45773d468614
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
2019年06月18日 阅读 5678

不再写 break 和 continue 了

break 与 some、every

个人平时喜欢用 forEach 来代替 for 循环。但有时发现实现过程中,需要使用 break。这时,一般又得切换回 for 循环。譬如遇见如下的逻辑:

let arr = [1, 2, 3, 4, 5]
let text = ''
for (let v of arr) {
  if (v === 3) { 
    break
  }
  text += v + ','
}
console.log(text) // "1,2,"
复制代码

今天看文章时想到,既然 some 的实现逻辑本身就是短路的,即一旦返回 true 后续迭代就不再执行了,那么为啥不可以用 some 替换 forEach 呢?

let arr = [1, 2, 3, 4, 5]
let text = ''
arr.some(v => {
  if (v === 3) { 
    return true
  }
  text += v + ','
})
console.log(text) // "1,2,"
复制代码

一般情况下,我们用 some 都是要用它返回结果的。而这种没有拿其返回值做文章的做法,算是代码阅读的一个信号:原来只是简简单单利用了其循环罢了。当然,这么写代码可读性不是很高。但确实是替换掉 for 的一种方式。

类似地,every 也是短路的,当然也可以替代 break。不过要保证 break 之前的迭代返回 true

let arr = [1, 2, 3, 4, 5]
let text = ''
arr.every(v => {
  if (v === 3) { 
    return
  }
  text += v + ','
  return true
})
console.log(text) // "1,2,"
复制代码

本文发到朋友圈后,朋友说这样看起来不那么“pure”。建议用 reduce 实现,不考虑性能,毕竟空迭代不浪费多少性能。如果要替代 break 的逻辑,那么必须得用一些 flag 来实现,比如:

let arr = [1, 2, 3, 4, 5]
let text = arr.reduce(function(p, c) {
  if (this.break) {
    return p
  }
  // ...
  if (c === 3) {
    this.break = true
    return p
  }
  return p + c + ','
}.bind({}), '')
console.log(text) // "1,2,"
复制代码

另外关于“pure”,如果封装成函数后,是纯的了:

function formatter(arr) {
  let text = ''
  arr.some(v => {
    if (v === 3) { 
      return true
    }
    text += v + ','
  })
  return text
}
let arr = [1, 2, 3, 4, 5]
console.log(formatter(arr)) // "1,2,"
复制代码

也有网友留言,可以用递归方式来做。for 循环本身都是可以用递归来替换的。而一般 break 的条件,正好可以是一个递归出口。例如本例子的递归实现:

function formatter(arr, text = '', i = 0) {
  if (arr.length == 0 || arr[i] == '3') {
    return text
  }
  text += arr[i] + ','
  return formatter(arr, text, ++i)
}
let arr = [1, 2, 3, 4, 5]
console.log(formatter(arr)) // "1,2,"
复制代码

continue 与 return

至于 continue 嘛。。。

let arr = [1, 2, 3, 4, 5]
let text = ''
for (let v of arr) {
  if (v === 3) { 
    continue
  }
  text += v + ','
}
console.log(text) // "1,2,4,5,"
复制代码

答案意外简单,forEach 直接 return 就好了:

let arr = [1, 2, 3, 4, 5]
let text = ''
arr.forEach(v => {
  if (v === 3) { 
    return
  }
  text += v + ','
})
console.log(text) // "1,2,4,5,"
复制代码

如果 continuebreak 同时存在呢?譬如:

let arr = [1, 2, 3, 4, 5]
let text = ''
for (let v of arr) {
  if (v === 2) {
    continue
  }
  if (v === 4) { 
    break
  }
  text += v + ','
}
console.log(text) // "1,3,"
复制代码

只用 some 就好了,反正数组那几个 API,本质上都是 for 循环。

let arr = [1, 2, 3, 4, 5]
let text = ''
arr.some(v => {
  if (v === 2) {
    return
  }
  if (v === 4) { 
    return true
  }
  text += v + ','
})
console.log(text) // "1,3,"
复制代码

some 和 every 需要注意的地方

some 函数用来判断数组中,是否至少有一个元素满足回调函数的条件。此时回调函数可以称为谓词函数(即判断是不是的意思)。规范文档some 是这么实现的:

点击展开

用 JS 模拟,其核心逻辑如下:

Array.prototype.some = function(callbackfn, thisArg) {
  let len = Number(this.length)
  let k = 0;
  while(k < len) {
    let Pk = String(k)
    if (Pk in this) {
      let kValue = this[Pk]
      if (callbackfn.call(thisArg, kValue, k, this)) {
        return true
      }
    }
    k++
  }
  return false
}
复制代码

可以看出,遇到回调返回值是 true 的话,函数就直接返回、结束了。这是种短路算法,并不是所有回调都执行一遍,然后再最后求所有与值。every 也类似,不过与之相反,遇到回调返回值是 false 时,整体就直接返回 false 了。

从实现上表达出的语义来讲,some 是在说:有一个成功,我就成功,而 every 是在说:有一个失败,我就失败

另外要强调一点,对于稀疏数组,不存在的索引值时,回调函数是不执行的。例如下例子中回调函数只执行了 3 遍(其他 API 也类似)。

let arr = [1, 2, 3]
delete arr[1]
arr[5] = 6
console.log("1" in arr) // false
console.log(arr) // [1, empty, 3, empty × 2, 6]
arr.some(v => {
  console.log(v) // 1 3 6
})
复制代码

因此空数组,不管回调函数如何写,其结果仍是 false

[].some(_ => true) // false
复制代码

Recommend

  • 52
    • www.v2ex.com 6 years ago
    • Cache

    再写贸易战和房价的问题。

    投资 - @xiaozuo - 刚刚写了一篇:https://www.v2ex.com/t/474811#reply0再写一下贸易战与楼市。这两个是目前最火的两个话题之一。先说一下积极的。我认为无论怎么打贸易战,中国

  • 84
    • zhuanlan.zhihu.com 5 years ago
    • Cache

    再写一本 Flask 书 - 知乎

  • 27
    • www.v2ex.com 5 years ago
    • Cache

    2020 不会再写任何一行代码

    写周报 - @Whsiqi - 以下内容是我对我自己吐槽,发出来让大家看看而已,不喜欢也不敢和各位大佬争执背景:19 岁,性格孤僻不合群,不会与人交流,找不到对象,依靠着写代码的自娱自乐,苟且活着的样子。2019 付出惨痛代

  • 37
    • www.cnblogs.com 5 years ago
    • Cache

    不要再写这样的神级代码了!

    JDK8提供的Stream虽然好用,Lambda虽然简洁,但一定不能 滥用 ,我举一个实际遇到的例子(已做脱敏处理): Map<Long, List<Student>> studentMap = students.stream().collect(Collectors.gro...

  • 16

    代码洁癖狂们!看到一个类中有几十个 if-else 是不是很抓狂?设计模式学了用不上吗?面试的时候问你,你只能回答最简单的单例模式,问你有没有用过反射之类的高级特性,回答也是否吗?

  • 65

    背景软件开发过程中,不可避免的是需要处理各种异常,就我自己来说,至少有一半以上的时间都是在处理各种异常情况,所以代码中就会出现大量的try {...} catch {...} finally {...} 代码块,不仅有大量的冗余代码,而且还影响代码的可读性。比较下面两张图,看看您...

  • 37
    • www.solidot.org 4 years ago
    • Cache

    Linus Torvalds 称他不再写代码了

  • 7

    As previous halving cycles along with the fundamental nature of bitcoin show, the BTC price is set to break $60,000 and go parabolic in 2021. The price of Bitcoin has been consolidating for the last two months, and on-chain analytics a...

  • 11
    • www.yunyingpai.com 3 years ago
    • Cache

    文案别再写「绝绝子」了

    文案别再写「绝绝子」了 ...

  • 11

    最近犯闲,想再写点啥项目,有推荐的吗? - V2EX V2EX  ›  Go 编程语言 最近犯闲,想再写点啥项目,有推荐的吗?  

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK