原文:https://hackernoon.com/rethinking-javascript-break-is-the-goto-of-loops-51b27b1c85f8#.k2oyppp5i
在我的上一篇文章 Death of the for Loop中蚊俺,我試圖去說服你放棄使用for
循環(huán)改用函數(shù)式的解決方案明未。反過來琅束,你提出了一個很好的問題,那么for循環(huán)中break
怎么辦污筷?
break 會相當于循環(huán)中的
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畴嘶,標記與break
和continue
也是相互對應的。因為break
和continue
來自于相同標記組集晚,這也導致了它們和GOTO
很像窗悯。
JavaScript標簽,break和continue是GOTO和非結(jié)構(gòu)化編程時代的遺留
“但是如果它沒有傷害任何人偷拔,那么我們?yōu)槭裁床话阉粝抡Z法中蒋院,而我們可以選擇其他的方案?”
我們?yōu)槭裁聪拗莆覀內(nèi)绾尉帉戃浖?
這個聽上去有些違背直覺莲绰,但是限制是一個好事欺旧。限制我們使用GOTO
就是一個很好的例子。我們也很歡迎限制我們的“use strict”
蛤签,甚至批評不使用它的人辞友。
“l(fā)imitations can make things better. A lot better. “— Charles Scalfani
限制(規(guī)則)可以使我們寫出更好的代碼。
為什么編程需要限制
_限制使藝術(shù)震肮,設計称龙,生活更美好._medium.com
我們對于break的選擇是什么?
我不是要做一個虛有其表的事情,但是也沒有一個方案可以適合所有的情況戳晌。 這是一個完全不同的編程方式鲫尊。 一個完全不同的思考方式。函數(shù)式編程的思想沦偎。
有一個好消息是疫向,有很多的庫和工具可以幫助我們咳蔚,例如Lodash, Ramda, lazy.js, 遞歸等等
我們將從一個簡單的cats集合和一個isKitten
函數(shù)開始,這些將在下面所有的例子中被用到搔驼。
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
循環(huán)的例子開始谈火。它會遍歷我們的cats,然后當找到第一只小貓的時候退出循環(huán)匙奴。
var firstKitten
for (var i = 0; i < cats.length; i++) {
if (isKitten(cats[i])) {
firstKitten = cats[i]
break
}
}
現(xiàn)在堆巧,讓我們和lodash中一個相同作用的例子做比較。
const firstKitten = _.find(cats, isKitten)
這個例子相當?shù)暮唵纹镁=酉聛碜屛覀儑L試一些邊緣情況吧〉簦現(xiàn)在我們改為遍歷cat集合,然后選出前5只小貓哗伯,然后退出循環(huán)荒揣。
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是一個很好的庫也可以坐很多的事情,但是有時候你需要一些其他更加專業(yè)的工具焊刹。這里我們介紹一個新朋友, lazy.js. “像Underscore系任,但是更加偷懶”。但是偷懶就是我們想要的.
const result = Lazy(cats)
.filter(isKitten)
.take(5)
困難的方法
庫都是有趣的虐块,但是有時候真正有趣的是從頭開始創(chuàng)造東西俩滥。
所以我們可以創(chuàng)建一個通用的函數(shù),讓它可以像filter
一樣使用也可以增加限制的功能贺奠。
第一步就是把我們上面寫的邊緣情況的for循環(huán)封裝在一個函數(shù)中霜旧。
接下來,讓我們是這個函數(shù)更加通用并且遍歷所有cat具體的內(nèi)容儡率。使用limit
來代替5
,predicate
來代替isKitten
挂据,list
來代替cats
。然后把這些作為函數(shù)的參數(shù)儿普。
現(xiàn)在我們有了一個可用的且可重復的takeFirst
函數(shù)崎逃,這個可以讓我們完全不用去關(guān)心我們cat的邏輯實現(xiàn)!
我們的函數(shù)現(xiàn)在依舊還是一個純函數(shù)眉孩。也就是說函數(shù)的輸出只和輸入的參數(shù)有關(guān)个绍。如果傳入相同的參數(shù),一定會得到相同的結(jié)果浪汪。
現(xiàn)在我們已經(jīng)還是有那個骯臟的for
循環(huán)障贸,所以讓我們繼續(xù)重構(gòu)。下一步就是把i
和newList
放入?yún)?shù)列表吟宦。
當limit
變?yōu)?的時候 (limit
會在遞歸過程中減少)或者是遍歷完了列表,我們希望可以退出遞歸(isDone
)涩维。
如果遞歸還在進行殃姓,我們將會核對是否有符合我們的過濾條件predicate
的值袁波。如果當前值符合過濾條件,我們會調(diào)用takeFirst
蜗侈,減少limit
并把當前值保存在我們的newList
中篷牌,否者,移動到列表的下一個值踏幻。
如果你還沒有看過Rethinking JavaScript: The if statement枷颊,它會解釋這個用三元表達式代替if
'的最后一步。
Rethinking JavaScript: The if statement
_Thinking functionally has opened my mind about programming._medium.com
現(xiàn)在我們像下面這樣調(diào)用我們的新方法:
const first5Kittens = takeFirst(5, isKitten, cats)
為了兼容更多的情況该面,我們可以柯里化takeFirst
夭苗,然后使用它去創(chuàng)建一些其他的函數(shù)(關(guān)于柯里化的介紹在另一篇文章中)
const first5 = takeFirst(5)
const getFirst5Kittens = first5(isKitten)
const first5Kittens = getFirst5Kittens(cats)
總結(jié)
現(xiàn)在有很多優(yōu)秀的庫例如 lodash, ramda和lazy.js供我們使用。但是如果我們足夠大膽隔缀,也可以使用遞歸來創(chuàng)建我們自己的函方法题造。
我必須要警告雖然takeFirst
看上去很酷 但是使用遞歸是有得也有失的. 遞歸在Javascript中是很危險的,它很容易就會導致超出最大調(diào)用堆棧大小
的報錯猾瘸。
我將會在我的下一篇文章中重寫JavaScript的遞歸界赔。敬請關(guān)注。
我知道這只是一件小事牵触,但是當我收到來自Medium和Twitter (@joelnet)的follow通知時會使我很開心淮悼。當然,如果你覺得我實在胡說八道揽思,你也可以在下面的討論區(qū)告訴我袜腥。
Cheers!