原文自工程師Enmanuel Durán博客, 傳送門
最近(或者不是最近,這徹底取決於您何時閱讀這邊文章),我正在跟個人團隊夥伴討論如何去處理這種須要根據不一樣的值去處理不一樣的狀況的方法,一般對於這種狀況下,人們喜歡使用switch語句或者使用不少if搭配else if條件。在本文中我將重點介紹第三種方式(我更爲喜歡的方法),即便用對象進行快速地查找。javascript
switch語句容許咱們根據傳遞的表達式的值來執行表達式並執行某些特定的操做,一般當你學習編寫代碼和算法時,你會發現能夠將它專門用於多種值的狀況,你開始使用它,它看起來很好,你很快意識到它給了你很大的自由,耶!可是要當心,自由度越大責任感也就越大。java
讓咱們快速瞭解一下典型的switch語句是怎麼樣的:git
switch (expression) { case x: { /* Your code here */ break; } case y: { /* Your code here */ break; } default: { /* Your code here */ } }
很好,如今有一些你可能不知道須要注意的事情:github
break關鍵字容許咱們在知足條件時中止執行塊。若是不將break關鍵字添加到switch語句,則不會拋出錯誤。若是咱們不當心忘記break的話,可能意味着在執行代碼的時候你甚至不知道代碼已經正在執行中了,這還會在調試問題時增長實現結果的的不一致性、突變、內存泄漏和複雜度等問題。咱們來看看這個問題的一種表示形式:web
switch ('first') { case 'first': { console.log('first case'); } case 'second': { console.log('second case'); } case 'third': { console.log('third case'); break; } default: { console.log('infinite'); } }
若是你在控制檯中執行這段代碼,你會看到輸出是算法
firt case second case third case
switch語句在第二種和第三種狀況下也會執行,即便第一種狀況已是正確的,而後它在第三種狀況塊中找到關鍵字break並中止執行,控制檯中沒有警告或錯誤讓你知道它,這會讓你認爲這是預期的行爲。express
在javascript中大括號表明着代碼塊,由於自ECMAscript 2015咱們可使用關鍵字聲明塊編譯變量,如const或let(但對於switch來講並非很好),由於大括號不是強制性的,重複聲明會致使錯誤變量,讓咱們看看當咱們執行下面的代碼時會發生什麼:vim
switch ('second') { case 'first': let position = 'first'; console.log(position); break; case 'second': let position = 'second'; console.log(position); break; default: console.log('infinite'); }
咱們會獲得:安全
Uncaught SyntaxError: Identifier 'position' has already been declared數據結構
這裏將會返回一個錯誤,由於變量position已經在第一種狀況下聲明過了,而且因爲它沒有大括號,因此在第二種狀況下嘗試聲明它,它已經存在了。
如今想象使用帶有不一致break關鍵字和大括號的switch語句時會發生什麼事:
switch ('first') { case 'first': let position = 'first'; console.log(position); case 'second': console.log(`second has access to ${position}`); position = 'second'; console.log(position); default: console.log('infinite'); }
控制檯將輸出如下內容:
first second has access to first second infinite
試想一下,由此而引發的錯誤和突變是如此之多,其可能性是無窮無盡的……無論怎樣,switch語句已經講夠了,咱們來這裏是爲了討論一種不一樣的方法,咱們來這裏是爲了討論對象。
對象查找速度很快,隨着它們的大小增加它們也會更快,它們也容許咱們將數據表示爲對於條件執行很是有用的鍵值對。
讓咱們從簡單的switch示例開始,讓咱們假設咱們須要有條件地保存和返回一個字符串的情景,並使用咱們的對象:
const getPosition = position => { const positions = { first: 'first', second: 'second', third: 'third', default: 'infinite' }; return positions[position] || positions.default; }; const position = getPosition('first'); // Returns 'first' const otherValue = getPosition('fourth'); // Returns 'infinite'
這能夠作一樣類型的工做,若是你想進一步的壓縮簡化代碼,咱們能夠利用箭頭函數:
const getPosition = position => ({ first: 'first', second: 'second', third: 'third' }[position] || 'infinite'); const positionValue = getPosition('first'); // Returns 'first' const otherValue = getPosition('fourth'); // Returns 'infinite'
這與前面的實現徹底相同,咱們在更少的代碼行中實現了更緊湊的解決方案。
如今讓咱們更實際一點,不是咱們寫的全部條件都會返回簡單的字符串,其中不少會返回布爾值,執行函數等等。
我喜歡建立返回類型一致的值的函數,可是,因爲javascript是動態類型語言,所以可能存在函數可能返回動態類型的狀況,所以我將在此示例中考慮這一點,若是找不到鍵,我將建立一個返回布爾值,未定義或字符串的函數。
const isNotOpenSource = language => ({ vscode: false, sublimetext: true, neovim: false, fakeEditor: undefined }[language] || 'unknown'); const sublimeState = isNotOpenSource('sublimetext'); // Returns true
看起來不錯,對吧?別急,好像咱們有一個問題......若是咱們調用帶有參數的函數,會發生什麼'vscode'或fakeEditor不是?嗯,讓咱們來看看:
對於key爲fakeEditor也會有一樣的問題
Oh no, 好吧,不要驚慌,讓咱們來解決這個問題:
const isNotOpenSource = editor => { const editors = { vscode: false, sublimetext: true, neovim: false, fakeEditor: undefined, default: 'unknown' }; return editor in editors ? editors[editor] : editors.default; }; const codeState = isNotOpenSource('vscode'); // Returns false const fakeEditorState = isNotOpenSource('fakeEditor'); // Returns undefined const sublimeState = isNotOpenSource('sublimetext'); // Returns true const webstormState = isNotOpenSource('webstorm'); // Returns 'unknown'
這就解決了問題,可是......我但願大家問本身一件事:這真的是問題所在嗎?我認爲咱們應該更關心爲何咱們須要一個返回布爾值,未定義值或字符串的函數,這裏存在嚴重的不一致性,不管如何,對於這樣一個很是棘手的狀況這也只是一個可能的解決方案。
咱們繼續講函數,一般咱們會發現咱們須要根據參數來執行一個函數,假設咱們須要根據輸入的類型來解析一些輸入值,若是解析器沒有註冊,咱們只返回值:
const getParsedInputValue = type => { const emailParser = email => `email, ${email}`; const passwordParser = password => `password, ${password}`; const birthdateParser = date => `date , ${date}`; const parsers = { email: emailParser, password: passwordParser, birthdate: birthdateParser, default: value => value }; return parsers[type] || parsers.default; }; // We select the parser with the type and then passed the dynamic value to parse const parsedEmail = getParsedInputValue('email')('myemail@gmail.com'); // Returns email, myemail@gmail.com const parsedName = getParsedInputValue('name')('Enmanuel'); // Returns 'Enmanuel'
若是咱們有一個相似的函數返回另外一個函數但此次沒有參數,咱們能夠改進代碼,以便在調用第一個函數時直接返回,如:
const getValue = type => { const email = () => 'myemail@gmail.com'; const password = () => '12345'; const parsers = { email, password, default: () => 'default' }; return (parsers[type] || parsers.default)(); // we immediately invoke the function here }; const emailValue = getValue('email'); // Returns myemail@gmail.com const passwordValue = getValue('name'); // Returns default
Switch語句容許咱們爲多個條件定義公共代碼塊。
switch (editor) { case 'atom': case 'sublime': case 'vscode': return 'It is a code editor'; break; case 'webstorm': case 'pycharm': return 'It is an IDE'; break; default: return 'unknown'; }
咱們如何使用對象來處理它?咱們能夠在下一個方面作到這一點:
const getEditorType = type => { const itsCodeEditor = () => 'It is a code editor'; const itsIDE = () => 'It is an IDE'; const editors = { atom: itsCodeEditor, sublime: itsCodeEditor, vscode: itsCodeEditor, webstorm: itsIDE, pycharm: itsIDE, default: () => 'unknown' }; return (editors[type] || editors.default)(); }; const vscodeType = getEditorType('vscode');
如今咱們有一種方法:
正如預期的那樣,全部的方法都有其缺點,這一個也不例外。
本文不打算改變你的編碼風格或讓你中止使用switch語句,它只是試圖提升你對switch語句的認識,以便它能夠正確使用,並開放你的思想探索新的替代方案,在這種狀況下,我已經分享了我喜歡使用的方法,但還有更多,例如,你可能想看一個稱爲模式匹配的ES6提案,若是你不喜歡它,你能夠繼續探索。
好的開發將來,就是這樣,我但願你喜歡這篇文章,若是你這樣作,你可能會喜歡這篇關於工廠模式的文章。此外,不要忘記分享和點贊,你能夠在twitter上找到我或經過個人電子郵件duranenmanuel@gmail.com聯繫我,下一個見。
閱讀EnmaScript.com上發佈的原始文章
本文介紹了一種使用對象去代替咱們以前用switch和繁瑣的if else語句的方法。其實,不少狀況下咱們能夠利用對象與其餘組合搭配寫出更爲高效或可維護的代碼。固然,如何去靈活地使用對象去處理一些對應的狀況,仍是靠咱們本身。好的,這篇就總結到這了,不知道對大家有什麼啓發。相信會給到一些幫助給讀者,咱們可不是一個只會if else的工程師,哈哈~