摘要:注遍歷器接口的目的,就是為所有數據結構,提供了一種統一的訪問機制,即循環詳見下文。屬性屬性返回結構的成員總數。返回所有成員的遍歷器。另一種情況是,的鍵名有非字符串,這時可以選擇轉為數組。復雜判斷的更優雅寫法引用文字阮一峰,入門
前提
我們編寫js代碼時經常遇到復雜邏輯判斷的情況,通常大家可以用if/else或者switch來實現多個條件判斷,但這樣會有個問題,隨著邏輯復雜度的增加,代碼中的if/else/switch會變得越來越臃腫,越來越看不懂,那么如何更優雅的寫判斷邏輯,本文帶你試一下。
if/else方式/** * @param {number} status 活動狀態:1 開團進行中 2 開團失敗 3 商品售罄 4 開團成功 5 系統取消 */ const statusChange = (status)=>{ if(status == 1){ sendLog("processing") jumpTo("IndexPage") }else if(status == 2 && status == 3){ sendLog("fail") jumpTo("FailPage") }else if(status == 4){ sendLog("success") jumpTo("SuccessPage") }else if(status == 5){ sendLog("cancel") jumpTo("CancelPage") }else { sendLog("other") jumpTo("Index") } } const sendLog = (log) => { document.write("sendLog:"+log+"switch方式
"); } const jumpTo = (page) => { document.write("jumpTo:"+page+"
"); document.write("----------------------
") }
/** * @param {number} status 活動狀態:1 開團進行中 2 開團失敗 3 商品售罄 4 開團成功 5 系統取消 */ const statusChange = (status)=>{ switch (status){ case 1: sendLog("processing") jumpTo("IndexPage") break case 2: case 3: sendLog("fail") jumpTo("FailPage") break case 4: sendLog("success") jumpTo("SuccessPage") break case 5: sendLog("cancel") jumpTo("CancelPage") break default: sendLog("other") jumpTo("Index") break } }一元判斷時:存到Object里
const actions = { "1": ["processing","IndexPage"], "2": ["fail","FailPage"], "3": ["fail","FailPage"], "4": ["success","SuccessPage"], "5": ["cancel","CancelPage"], "default": ["other","Index"], } /** * @param {number} status 活動狀態:1開團進行中 2開團失敗 3 商品售罄 4 開團成功 5 系統取消 */ const statusChange = (status)=>{ let action = actions[status] || actions["default"], logName = action[0], pageName = action[1] sendLog(logName) jumpTo(pageName) }一元判斷時:存到Map里
const actions = new Map([ [1, ["processing","IndexPage"]], [2, ["fail","FailPage"]], [3, ["fail","FailPage"]], [4, ["success","SuccessPage"]], [5, ["cancel","CancelPage"]], ["default", ["other","Index"]] ]) /** * @param {number} status 活動狀態:1 開團進行中 2 開團失敗 3 商品售罄 4 開團成功 5 系統取消 */ const statusChange = (status)=>{ let action = actions.get(status) || actions.get("default") sendLog(action[0]) jumpTo(action[1]) }
這樣寫用到了es6里的Map對象,Map對象和Object對象有什么區別呢?
一個對象通常都有自己的原型,所以一個對象總有一個"prototype"鍵。
一個對象的鍵只能是字符串,但一個Map的鍵可以是任意值。
你可以通過size屬性很容易地得到一個Map的鍵值對個數,而對象的鍵值對個數只能手動確認。
例子,if-else寫法
/* * @param {string} identity 身份標識:guest客態 master主態 */ const statusChange = (status,identity)=>{ if(identity == "guest"){ if(status == 1){ //do sth }else if(status == 2){ //do sth }else if(status == 3){ //do sth }else if(status == 4){ //do sth }else if(status == 5){ //do sth }else { //do sth } }else if(identity == "master") { if(status == 1){ //do sth }else if(status == 2){ //do sth }else if(status == 3){ //do sth }else if(status == 4){ //do sth }else if(status == 5){ //do sth }else { //do sth } } }多元判斷時:將條件拼接成字符串存到Map里
將上述判斷用map方式實現
const actions = new Map([ ["guest_1", ()=>{ console.log("guest_1"); }], ["guest_2", ()=>{ console.log("guest_2"); }], ["guest_3", ()=>{ console.log("guest_3"); }], ["guest_4", ()=>{ console.log("guest_4"); }], ["guest_5", ()=>{ console.log("guest_5"); }], ["master_1", ()=>{ console.log("master_1"); }], ["master_2", ()=>{ console.log("master_2"); }], ["master_3", ()=>{ console.log("master_3"); }], ["master_4", ()=>{ console.log("master_4"); }], ["master_5", ()=>{ console.log("master_5"); }], ["default", ()=>{ console.log("default"); }], ]) const statusChange = (identity,status)=>{ let action = actions.get(`${identity}_${status}`) || actions.get("default") action.call(this) }多元判斷時:將條件拼接成字符串存到Object里
上述代碼核心邏輯是:把兩個條件拼接成字符串,并通過以條件拼接字符串作為鍵,以處理函數作為值的Map對象進行查找并執行,這種寫法在多元條件判斷時候尤其好用。
當然上述代碼如果用Object對象來實現也是類似的:
const actions = { "guest_1": ()=>{ console.log("guest_1"); }, "guest_2": ()=>{ console.log("guest_2"); }, "guest_3": ()=>{ console.log("guest_3"); }, "guest_4": ()=>{ console.log("guest_4"); }, "guest_5": ()=>{ console.log("guest_5"); }, "master_1": ()=>{ console.log("master_1"); }, "master_2": ()=>{ console.log("master_2"); }, "master_3": ()=>{ console.log("master_3"); }, "master_4": ()=>{ console.log("master_4"); }, "master_5": ()=>{ console.log("master_5"); }, "default": ()=>{ console.log("default"); }, } const statusChange = (identity,status)=>{ let action = actions[`${identity}_${status}`] || actions["default"] action.call(this) }多元判斷時:將條件存為Object存到Map里
const actions = new Map([ [{identity:"guest",status:1},()=>{ console.log("guest_1"); }], [{identity:"guest",status:2},()=>{ console.log("guest_2"); }], [{identity:"guest",status:3},()=>{ console.log("guest_3"); }], [{identity:"guest",status:4},()=>{ console.log("guest_4"); }], ]) const statusChange = (identity,status)=>{ let action = [...actions].filter(([key,value])=>(key.identity == identity && key.status == status)) action.forEach(([key,value])=>value.call(this)) }
我們現在再將難度升級一點點,假如guest情況下,status1-4的處理邏輯都一樣怎么辦,最差的情況是這樣:
const actions = new Map([ [{identity:"guest",status:1},()=>{/* functionA */}], [{identity:"guest",status:2},()=>{/* functionA */}], [{identity:"guest",status:3},()=>{/* functionA */}], [{identity:"guest",status:4},()=>{/* functionA */}], [{identity:"guest",status:5},()=>{/* functionB */}], //... ])
好一點的寫法是將處理邏輯函數進行緩存:
const actions = ()=>{ const functionA = ()=>{/*do sth*/} const functionB = ()=>{/*do sth*/} return new Map([ [{identity:"guest",status:1},functionA], [{identity:"guest",status:2},functionA], [{identity:"guest",status:3},functionA], [{identity:"guest",status:4},functionA], [{identity:"guest",status:5},functionB], //... ]) } const statusChange = (identity,status)=>{ let action = [...actions].filter(([key,value])=>(key.identity == identity && key.status == status)) action.forEach(([key,value])=>value.call(this)) }多元判斷時:將條件寫作正則存到Map里
假如判斷條件變得特別復雜,比如identity有3種狀態,status有10種狀態,那你需要定義30條處理邏輯,而往往這些邏輯里面很多都是相同的,這似乎也是筆者不想接受的,那可以這樣實現:
const actions = ()=>{ const functionA = ()=>{/*do sth*/} const functionB = ()=>{/*do sth*/} return new Map([ [/^guest_[1-4]$/,functionA], [/^guest_5$/,functionB], ]) } const statusChange = (identity,status)=>{ let action = [...actions()].filter(([key,value])=>(key.test(`${identity}_${status}`))) action.forEach(([key,value])=>value.call(this)) }
這里Map的優勢更加凸顯,可以用正則類型作為key了,這樣就有了無限可能,假如需求變成,凡是guest情況都要發送一個日志埋點,不同status情況也需要多帶帶的邏輯處理,那我們可以這樣寫:
onst actions = ()=>{ const functionA = ()=>{/*do sth*/} const functionB = ()=>{/*do sth*/} const functionC = ()=>{/*send log*/} return new Map([ [/^guest_[1-4]$/,functionA], [/^guest_5$/,functionB], [/^guest_.*$/,functionC], //... ]) } const statusChange = (identity,status)=>{ let action = [...actions].filter(([key,value])=>(key.test(`${identity}_${status}`))) action.forEach(([key,value])=>value.call(this)) }
也就是說利用數組循環的特性,符合正則條件的邏輯都會被執行,那就可以同時執行公共邏輯和多帶帶邏輯,因為正則的存在,你可以打開想象力解鎖更多的玩法,本文就不贅述了。
擴展 es6 Map數據結構Map 數據結構類似于對象,也是鍵值對的集合,但是“鍵”的范圍不限于字符串,各種類型的值(包括對象)都可以當作鍵。也就是說,Object 結構提供了“字符串—值”的對應,Map 結構提供了“值—值”的對應,是一種更完善的 Hash 結構實現。如果你需要“鍵值對”的數據結構,Map 比 Object 更合適。
例子
const m = new Map(); const o = {p: "Hello World"}; m.set(o, "content") m.get(o) // "content" m.has(o) // true m.delete(o) // true m.has(o) // false
Map 任何具有 Iterator 接口、且每個成員都是一個雙元素的數組的數據結構作為參數,也就是說,數組,Set和Map都可以用來生成新的 Map。
注:遍歷器(Iterator) 接口的目的,就是為所有數據結構,提供了一種統一的訪問機制,即for...of循環(詳見下文)。當使用for...of循環遍歷某種數據結構時,該循環會自動去尋找 Iterator 接口。
原生具備 Iterator 接口的數據結構如下:Array、Map、Set、String、TypedArray、函數的 arguments 對象、NodeList 對象
// 數組作為參數 const map = new Map([ ["name", "張三"], ["title", "Author"] ]); map.size // 2 map.has("name") // true map.get("name") // "張三" map.has("title") // true map.get("title") // "Author" // set數據結構作為參數 const set = new Set([ ["foo", 1], ["bar", 2] ]); const m1 = new Map(set); m1.get("foo") // 1 // map作為參數 const m2 = new Map([["baz", 3]]); const m3 = new Map(m2); m3.get("baz") // 3
如果對同一個鍵多次賦值,后面的值將覆蓋前面的值。
const map = new Map(); map .set(1, "aaa") .set(1, "bbb"); map.get(1) // "bbb"
如果讀取一個未知的鍵,則返回undefined。
new Map().get("asfddfsasadf") // undefined
注意,只有對同一個對象的引用,Map 結構才將其視為同一個鍵。這一點要非常小心。
const map = new Map(); map.set(["a"], 555); map.get(["a"]) // undefined
上面代碼的set和get方法,表面是針對同一個鍵,但實際上這是兩個值,內存地址是不一樣的,因此get方法無法讀取該鍵,返回undefined。
同理,同樣的值的兩個實例,在 Map 結構中被視為兩個鍵。
const map = new Map(); const k1 = ["a"]; const k2 = ["a"]; map .set(k1, 111) .set(k2, 222); map.get(k1) // 111 map.get(k2) // 222
上面代碼中,變量k1和k2的值是一樣的,但是它們在 Map 結構中被視為兩個鍵。
由上可知,Map 的鍵實際上是跟內存地址綁定的,只要內存地址不一樣,就視為兩個鍵。這就解決了同名屬性碰撞(clash)的問題,我們擴展別人的庫的時候,如果使用對象作為鍵名,就不用擔心自己的屬性與原作者的屬性同名。
如果 Map 的鍵是一個簡單類型的值(數字、字符串、布爾值),則只要兩個值嚴格相等,Map 將其視為一個鍵
比如0和-0就是一個鍵,布爾值true和字符串true則是兩個不同的鍵。
另外,undefined和null也是兩個不同的鍵。雖然NaN不嚴格相等于自身,但 Map 將其視為同一個鍵。
let map = new Map(); map.set(-0, 123); map.get(+0) // 123 map.set(true, 1); map.set("true", 2); map.get(true) // 1 map.set(undefined, 3); map.set(null, 4); map.get(undefined) // 3 map.set(NaN, 123); map.get(NaN) // 123實例的屬性和操作方法
Map 結構的實例有以下屬性和操作方法。
(1)size 屬性
size屬性返回 Map 結構的成員總數。
const map = new Map(); map.set("foo", true); map.set("bar", false); map.size // 2
(2)set(key, value)
set方法設置鍵名key對應的鍵值為value,然后返回整個 Map 結構。如果key已經有值,則鍵值會被更新,否則就新生成該鍵。
const m = new Map(); m.set("edition", 6) // 鍵是字符串 m.set(262, "standard") // 鍵是數值 m.set(undefined, "nah") // 鍵是 undefined
set方法返回的是當前的Map對象,因此可以采用鏈式寫法。
let map = new Map() .set(1, "a") .set(2, "b") .set(3, "c");
(3)get(key)
get方法讀取key對應的鍵值,如果找不到key,返回undefined。
const m = new Map(); const hello = function() {console.log("hello");}; m.set(hello, "Hello ES6!") // 鍵是函數 m.get(hello) // Hello ES6!
(4)has(key)
has方法返回一個布爾值,表示某個鍵是否在當前 Map 對象之中。
const m = new Map(); m.set("edition", 6); m.set(262, "standard"); m.set(undefined, "nah"); m.has("edition") // true m.has("years") // false m.has(262) // true m.has(undefined) // true
(5)delete(key)
delete方法刪除某個鍵,返回true。如果刪除失敗,返回false。
const m = new Map(); m.set(undefined, "nah"); m.has(undefined) // true m.delete(undefined) m.has(undefined) // false
(6)clear()
clear方法清除所有成員,沒有返回值。
let map = new Map(); map.set("foo", true); map.set("bar", false); map.size // 2 map.clear() map.size // 0
遍歷方法
Map 結構原生提供三個遍歷器生成函數和一個遍歷方法。
keys():返回鍵名的遍歷器。
values():返回鍵值的遍歷器。
entries():返回所有成員的遍歷器。
forEach():遍歷 Map 的所有成員。
需要特別注意的是,Map 的遍歷順序就是插入順序。
const map = new Map([ ["F", "no"], ["T", "yes"], ]); for (let key of map.keys()) { console.log(key); } // "F" // "T" for (let value of map.values()) { console.log(value); } // "no" // "yes" for (let item of map.entries()) { console.log(item[0], item[1]); } // "F" "no" // "T" "yes" // 或者 for (let [key, value] of map.entries()) { console.log(key, value); } // "F" "no" // "T" "yes" // 下面的方法等同于使用map.entries() for (let [key, value] of map) { console.log(key, value); } // "F" "no" // "T" "yes"
上面代碼最后的那個例子,表示 Map 結構的默認遍歷器接口(Symbol.iterator屬性),就是entries方法。
map[Symbol.iterator] === map.entries // true Map 結構轉為數組結構,比較快速的方法是使用擴展運算符(...)。 const map = new Map([ [1, "one"], [2, "two"], [3, "three"], ]); [...map.keys()] // [1, 2, 3] [...map.values()] // ["one", "two", "three"] [...map.entries()] // [[1,"one"], [2, "two"], [3, "three"]] [...map] // [[1,"one"], [2, "two"], [3, "three"]]
結合數組的map方法、filter方法,可以實現 Map 的遍歷和過濾(Map 本身沒有map和filter方法)。
const map0 = new Map() .set(1, "a") .set(2, "b") .set(3, "c"); const map1 = new Map( [...map0].filter(([k, v]) => k < 3) ); // 產生 Map 結構 {1 => "a", 2 => "b"} const map2 = new Map( [...map0].map(([k, v]) => [k * 2, "_" + v]) ); // 產生 Map 結構 {2 => "_a", 4 => "_b", 6 => "_c"}
此外,Map 還有一個forEach方法,與數組的forEach方法類似,也可以實現遍歷。
map.forEach(function(value, key, map) { console.log("Key: %s, Value: %s", key, value); }); // %$表示字符串輸出 // Key: 1, Value: a // Key: 2, Value: b
forEach方法還可以接受第二個參數,用來綁定this。
const reporter = { report: function(key, value) { console.log("Key: %s, Value: %s", key, value); } }; map.forEach(function(value, key, map) { this.report(key, value); }, reporter);
上面代碼中,forEach方法的回調函數的this,就指向reporter。
與其他數據結構的互相轉換
(1)Map 轉為數組
前面已經提過,Map 轉為數組最方便的方法,就是使用擴展運算符(...)。
const myMap = new Map() .set(true, 7) .set({foo: 3}, ["abc"]); [...myMap] // [ [ true, 7 ], [ { foo: 3 }, [ "abc" ] ] ]
(2)數組 轉為 Map
將數組傳入 Map 構造函數,就可以轉為 Map。
new Map([ [true, 7], [{foo: 3}, ["abc"]] ]) // Map { // true => 7, // Object {foo: 3} => ["abc"] // }
(3)Map 轉為對象
如果所有 Map 的鍵都是字符串,它可以無損地轉為對象。
function strMapToObj(strMap) { let obj = Object.create(null); for (let [k,v] of strMap) { obj[k] = v; } return obj; } const myMap = new Map() .set("yes", true) .set("no", false); strMapToObj(myMap) // { yes: true, no: false }
如果有非字符串的鍵名,那么這個鍵名會被轉成字符串,再作為對象的鍵名。
(4)對象轉為 Map
function objToStrMap(obj) { let strMap = new Map(); for (let k of Object.keys(obj)) { strMap.set(k, obj[k]); } return strMap; } objToStrMap({yes: true, no: false}) // Map {"yes" => true, "no" => false}
(5)Map 轉為 JSON
Map 轉為 JSON 要區分兩種情況。一種情況是,Map 的鍵名都是字符串,這時可以選擇轉為對象 JSON。
function strMapToJson(strMap) { return JSON.stringify(strMapToObj(strMap)); } let myMap = new Map().set("yes", true).set("no", false); strMapToJson(myMap) // "{"yes":true,"no":false}"
另一種情況是,Map 的鍵名有非字符串,這時可以選擇轉為數組 JSON。
function mapToArrayJson(map) { return JSON.stringify([...map]); } let myMap = new Map().set(true, 7).set({foo: 3}, ["abc"]); mapToArrayJson(myMap) // "[[true,7],[{"foo":3},["abc"]]]"
(6)JSON 轉為 Map
JSON 轉為 Map,正常情況下,所有鍵名都是字符串。
function jsonToStrMap(jsonStr) { return objToStrMap(JSON.parse(jsonStr)); } jsonToStrMap("{"yes": true, "no": false}") // Map {"yes" => true, "no" => false}
但是,有一種特殊情況,整個 JSON 就是一個數組,且每個數組成員本身,又是一個有兩個成員的數組。這時,它可以一一對應地轉為 Map。這往往是 Map 轉為數組 JSON 的逆操作。
function jsonToMap(jsonStr) { return new Map(JSON.parse(jsonStr)); } jsonToMap("[[true,7],[{"foo":3},["abc"]]]") // Map {true => 7, Object {foo: 3} => ["abc"]}
JavaScript 復雜判斷的更優雅寫法引用文字
阮一峰,es6入門
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/104479.html
摘要:所以下面介紹一些函數式編程的知識和概念。函數式編程的一個明顯的好處就是這種聲明式的代碼,對于無副作用的純函數,我們完全可以不考慮函數內部是如何實現的,專注于編寫業務代碼。我會在下一篇文章中介紹函數式編程的更加高階一些的知識,例如等等概念。 一、引言 說到函數式編程,大家可能第一印象都是學院派的那些晦澀難懂的代碼,充滿了一大堆抽象的不知所云的符號,似乎只有大學里的計算機教授才會使用這些東...
摘要:所以下面介紹一些函數式編程的知識和概念。函數式編程的一個明顯的好處就是這種聲明式的代碼,對于無副作用的純函數,我們完全可以不考慮函數內部是如何實現的,專注于編寫業務代碼。 原文鏈接 引言 說到函數式編程,大家可能第一印象都是學院派的那些晦澀難懂的代碼,充滿了一大堆抽象的不知所云的符號,似乎只有大學里的計算機教授才會使用這些東西。在曾經的某個時代可能確實如此,但是近年來隨著技術的發展,函...
摘要:但是隨著程序邏輯越來越復雜,業務邏輯代碼跟代碼混到一起就變得越來越難以維護,所以就有了開發模式。其實只是給加了點糖上面這種在中寫類似代碼的語法被稱為。你可以理解為擴展版的。尤其是對一些相對還比較流行的框架或技術,更是如此。 這是《玩轉 React》系列的第三篇,看到本篇的標題,了解過 React 的同學可能已經大致猜到我要講什么了,本篇中要講的內容對于剛接觸 React 的同學來說,可...
摘要:但是隨著程序邏輯越來越復雜,業務邏輯代碼跟代碼混到一起就變得越來越難以維護,所以就有了開發模式。其實只是給加了點糖上面這種在中寫類似代碼的語法被稱為。你可以理解為擴展版的。尤其是對一些相對還比較流行的框架或技術,更是如此。 這是《玩轉 React》系列的第三篇,看到本篇的標題,了解過 React 的同學可能已經大致猜到我要講什么了,本篇中要講的內容對于剛接觸 React 的同學來說,可...
摘要:對前端來說,使用的場景不多,但是像微信端的對話系統的表情包,就使用到了一個特定的規則。我是一個前端,工作年了,現在失業,想進入騰訊工作,這是我的聯系方式這個正則雖 我發現有個別字符被這個編輯器給刷掉了,但是灰色區域顯示正常,以灰色區域代碼為準 什么玩意? 在我剛開始學習編程的時候,就聽過正則了,也聽說正則很牛逼,懂正則的更牛逼。但是苦于沒有人指點,也沒有使用正則的場景,自己看教程又懵逼...
閱讀 2130·2021-11-18 10:07
閱讀 3507·2021-09-04 16:48
閱讀 3214·2019-08-30 15:53
閱讀 1235·2019-08-30 12:55
閱讀 2453·2019-08-29 15:08
閱讀 3149·2019-08-29 15:04
閱讀 2879·2019-08-29 14:21
閱讀 2906·2019-08-29 11:21