反思JavaScript: 通过函数代替break

原文:https://hackernoon.com/rethinking-javascript-break-is-the-goto-of-loops-51b27b1c85f8#.k2oyppp5i

在我的上一篇文章 Death of the for Loop中,我试图去说服你放弃使用for 循环改用函数式的解决方案。反过来,你提出了一个很好的问题,那么for循环中break怎么办?

break 会相当于循环中的GOTO,我们应该避免使用。

break 应该像GOTO一样被废弃。

你可能会想“算了吧Joel,你这只是耸人听闻,break怎么可能会像GOTO一样?”

// bad code. no copy paste.

outer:
  for (var i in outerList) {
inner:
    for (var j in innerList) {
      break outer;
    }
  }

我可以提供标记作为证明。在其他语言中,标记和GOTO是相互对应的。在JavaScript,标记与breakcontinue也是相互对应的。因为breakcontinue来自于相同标记组,这也导致了它们和GOTO很像。

JavaScript标签,break和continue是GOTO和非结构化编程时代的遗留

xkcd

“但是如果它没有伤害任何人,那么我们为什么不把它留下语法中,而我们可以选择其他的方案?”

我们为什么限制我们如何编写软件?

这个听上去有些违背直觉,但是限制是一个好事。限制我们使用GOTO就是一个很好的例子。我们也很欢迎限制我们的“use strict”,甚至批评不使用它的人。

“limitations can make things better. A lot better. “— Charles Scalfani

限制(规则)可以使我们写出更好的代码。

为什么编程需要限制
_限制使艺术,设计,生活更美好._medium.com

我们对于break的选择是什么?

我不是要做一个虚有其表的事情,但是也没有一个方案可以适合所有的情况。 这是一个完全不同的编程方式。 一个完全不同的思考方式。函数式编程的思想。

有一个好消息是,有很多的库和工具可以帮助我们,例如Lodash, Ramda, lazy.js, 递归等等

我们将从一个简单的cats集合和一个isKitten函数开始,这些将在下面所有的例子中被用到。

const cats = [
  { name: 'Mojo',    months: 84 },
  { name: 'Mao-Mao', months: 34 },
  { name: 'Waffles', months: 4 },
  { name: 'Pickles', months: 6 }
]

const isKitten = cat => cat.months < 7

让我们从一个我们熟悉的for循环的例子开始。它会遍历我们的cats,然后当找到第一只小猫的时候退出循环。

var firstKitten

for (var i = 0; i < cats.length; i++) {
  if (isKitten(cats[i])) {
    firstKitten = cats[i]
    break
  }
}

现在,让我们和lodash中一个相同作用的例子做比较。

const firstKitten = _.find(cats, isKitten)

这个例子相当的简单。接下来让我们尝试一些边缘情况吧。现在我们改为遍历cat集合,然后选出前5只小猫,然后退出循环。

var first5Kittens = []

// old-school edge case kitty loop
for (var i = 0; i < cats.length; i++) {
  if (isKitten(cats[i])) {
    first5Kittens.push(cats[i])

    if (first4Kittens.length >= 5) {
      break
    }
  }
}

简单的方式

lodash是一个很好的库也可以坐很多的事情,但是有时候你需要一些其他更加专业的工具。这里我们介绍一个新朋友, lazy.js. “像Underscore,但是更加偷懒”。但是偷懒就是我们想要的.

const result = Lazy(cats)
  .filter(isKitten)
  .take(5)

困难的方法

库都是有趣的,但是有时候真正有趣的是从头开始创造东西

所以我们可以创建一个通用的函数,让它可以像filter一样使用也可以增加限制的功能。

第一步就是把我们上面写的边缘情况的for循环封装在一个函数中。

接下来,让我们是这个函数更加通用并且遍历所有cat具体的内容。使用limit来代替5,predicate来代替isKittenlist来代替cats。然后把这些作为函数的参数。

现在我们有了一个可用的且可重复的takeFirst函数,这个可以让我们完全不用去关心我们cat的逻辑实现!

我们的函数现在依旧还是一个纯函数。也就是说函数的输出只和输入的参数有关。如果传入相同的参数,一定会得到相同的结果。

现在我们已经还是有那个肮脏的for循环,所以让我们继续重构。下一步就是把inewList放入参数列表。

limit变为0的时候 (limit会在递归过程中减少)或者是遍历完了列表,我们希望可以退出递归(isDone)。

如果递归还在进行,我们将会核对是否有符合我们的过滤条件predicate的值。如果当前值符合过滤条件,我们会调用takeFirst,减少limit并把当前值保存在我们的newList中,否者,移动到列表的下一个值。

如果你还没有看过Rethinking JavaScript: The if statement,它会解释这个用三元表达式代替if'的最后一步。

Rethinking JavaScript: The if statement
_Thinking functionally has opened my mind about programming._medium.com

现在我们像下面这样调用我们的新方法:

const first5Kittens = takeFirst(5, isKitten, cats)

为了兼容更多的情况,我们可以柯里化takeFirst,然后使用它去创建一些其他的函数(关于柯里化的介绍在另一篇文章中)

const first5 = takeFirst(5)
const getFirst5Kittens = first5(isKitten)

const first5Kittens = getFirst5Kittens(cats)

总结

现在有很多优秀的库例如 lodash, ramda和lazy.js供我们使用。但是如果我们足够大胆,也可以使用递归来创建我们自己的函方法。

我必须要警告虽然takeFirst看上去很酷 但是使用递归是有得也有失的. 递归在Javascript中是很危险的,它很容易就会导致超出最大调用堆栈大小的报错。

我将会在我的下一篇文章中重写JavaScript的递归。敬请关注。

我知道这只是一件小事,但是当我收到来自Medium和Twitter (@joelnet)的follow通知时会使我很开心。当然,如果你觉得我实在胡说八道,你也可以在下面的讨论区告诉我。

Cheers!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容

  • 感谢社区中各位的大力支持,译者再次奉上一点点福利:阿里云产品券,享受所有官网优惠,并抽取幸运大奖:点击这里领取 在...
    HetfieldJoe阅读 1,804评论 0 14
  • 函数参数的默认值 基本用法 在ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法。 上面代码检查函数l...
    呼呼哥阅读 3,363评论 0 1
  • 感谢社区中各位的大力支持,译者再次奉上一点点福利:阿里云产品券,享受所有官网优惠,并抽取幸运大奖:点击这里领取 你...
    HetfieldJoe阅读 732评论 0 2
  • 在这样一个春却似夏的季节里 总会掩饰不了对盛夏酷暑的渴望 还没近入四月的尾巴, 便脚踩一双凉鞋迫不及待冲下楼,手举...
    杜小遥阅读 286评论 0 1
  • 儿的班级家长微信群里上午班主任发了一组照片,班级文化墙于今日成立!热烈祝贺孩子们!感赏年轻的班主任陈老师年轻有为,...
    zhzhtydnkshyb阅读 253评论 3 1