摘要:這個時候查了下正則表達式的文檔文檔點擊這里,發(fā)現(xiàn)有一個方法,可以返回匹配成功的結(jié)果。那么我來總結(jié)下文章想表達的內(nèi)容對于具有固定格式的字符串,可以考慮使用正則表達式來識別和匹配。
今天在認真干(劃)活(水)的時候,看到群里有人發(fā)了一道頭條的面試題,就順便看了一下,發(fā)現(xiàn)挺有意思的,就決定分享給大家,并且給出我的解決方案和思考過程。
題目如下:
實現(xiàn)一個get函數(shù),使得下面的調(diào)用可以輸出正確的結(jié)果
const obj = { selector: { to: { toutiao: "FE Coder"} }, target: [1, 2, { name: "byted"}]}; get(obj, "selector.to.toutiao", "target[0]", "target[2].name"); // [ "FE Coder", 1, "byted"]
乍眼一看,這不就是實現(xiàn)一個lodash.get方法嗎?看上去好像很簡單。所以我就開始寫了第一個版本。思想其實很簡單,遍歷傳進來的參數(shù),使用split將每一個參數(shù)分隔開,然后遍歷取值,最終返回結(jié)果。
function get(data, ...args) { return args.map((item) => { const paths = item.split("."); let res = data; paths.map(path => res = res[path]); return res; }) }
一運行,果不其然,報錯了。
后來仔細看了一下提供的測試代碼,發(fā)現(xiàn)居然有target[0]這種東西。。居然還帶了個數(shù)組索引。
冷靜分析一下,對于后面帶了個索引的類型,比如"target[0]",我們肯定是要特殊對待的。所以,我們首先得先識別到這種特殊的類型,然后再對它進行額外處理。
這個時候,很快的就可以想到使用正則表達式來做這個事情。為什么呢?因為像這種帶有索引的類型,他們都有一個特色,就是有固定的格式:[num],那么我們只需要能構(gòu)造出可以匹配這種固定格式的正則,就可以解決這個問題。
對于這種格式,不難想到可以用這個正則表達式來做判斷:/[[0-9]+]/gi,可是我們還需要將匹配值取出來。這個時候查了下正則表達式的文檔(文檔點擊這里),發(fā)現(xiàn)有一個match方法,可以返回匹配成功的結(jié)果。那么就讓我們來做個測試:
const reg = /[[0-9]+]/gi; const str = "target[123123]"; const str1 = "target[]" if (reg.test(str)) { console.log("test success"); } if (!reg.test(str1)) { console.log("test fail"); } const matchResult = str.match(reg); console.log(matchResult); // ["[123123]"]
誒,我們現(xiàn)在已經(jīng)找到了解決這種問題的方法,那讓我們趕緊來繼續(xù)改進下代碼。
function get(data, ...args) { const reg = /[[0-9]+]/gi; return args.map((item) => { const paths = item.split("."); let res = data; paths.map((path) => { if (reg.test(path)) { const match = path.match(reg)[0]; // 將target[0]里的target儲存到cmd里 const cmd = path.replace(match, ""); // 獲取數(shù)組索引 const arrIndex = match.replace(/[[]]/gi, ""); res = res[cmd][arrIndex]; } else { res = res[path]; } }); return res; }); } const obj = { selector: { to: { toutiao: "FE Coder"} }, target: [1, 2, { name: "byted"}]}; console.log(get(obj, "selector.to.toutiao", "target[0]", "target[2].name"));
寫完趕緊運行一下,完美,輸出了正確的結(jié)果了。那么到這里就結(jié)束了?
改進可是總感覺有點不妥,感覺事情沒有那么簡單。一般來說,面試題除了考驗你解決問題的能力之外,可能還考驗著你思考問題的全面性、嚴謹性。像上面那種寫法,如果用戶傳入了一個不存在的path鏈或者一些其他特殊情況,就可能導致整個程序crash掉。想下lodash.get調(diào)用方式,即使你傳入了錯誤的path,他也可以幫你做處理,并且返回一個undefined。因此,我們還需要完善這個方法。
function get(data, ...args) { const reg = /[[0-9]+]/gi; return args.map((item) => { const paths = item.split("."); let res = data; paths.map(path => { try { if (reg.test(path)) { const match = path.match(reg)[0]; const cmd = path.replace(match, ""); const arrIndex = match.replace(/[[]]/gi, ""); res = res[cmd][arrIndex]; } else { res = res[path]; } } catch (err) { console.error(err); res = undefined; } }); return res; }); }
在這里,我們對每一個path的處理進行了try catch處理。若出錯了,則返回undefined。哇,這樣看起來就比較穩(wěn)了。
那么,有沒有別的解決方法呢?
群里有一個大佬提出了一種更簡單也很取巧的解決方案,就是通過構(gòu)建一個Function解決這個問題(Function的詳細介紹點擊這里)。由于代碼很簡單,我就直接貼出來了:
function get(data, ...args) { const res = JSON.stringify(data); return args.map((item) => (new Function(`try {return ${res}.${item} } catch(e) {}`))()); } const obj = { selector: { to: { toutiao: "FE Coder"} }, target: [1, 2, { name: "byted"}]}; console.log(get(obj, "selector.to.toutiao", "target[0]", "target[2].name", "asd"));
看完之后,就兩個字,牛逼。
這種方法我承認一開始我確實沒想到,確實是很奇技淫巧。不過仔細思考了下,其實很多框架都用到了這個奇技淫巧。比如說vue里,就使用new Function的方式來動態(tài)創(chuàng)建函數(shù),解決執(zhí)行動態(tài)生成的代碼的問題。
再比如說,F(xiàn)unction.prototype.bind方法里(我寫了個類似的bind方法:倉庫),也使用了Function來解決一些問題(fn.length丟失問題)。說明這個東西還是挺有用的,得學習了解一波,說不定哪天就用到了。
更新
有人提到了那種Function的方式?jīng)]辦法處理以下的處理:
let obj = {time : new Date(), a : "this is a", b : 30};
因為JSON.stringfy后,Date、Function和RegExp類型的變量都會失效。對于這種情況,評論區(qū)有個大佬(馮恒智)也提到了一種很好的解決方案:
function get(data, ...args) { return args.map((item) => (new Function("data",`try {return data.${item} } catch(e) {}`))(data)); }
除此之外,?代碼宇宙提出了另一種解決方案,就是將"target[0]"分為兩個key,也很簡單粗暴,就是將在split之前,將字符串里的"["替換為".",將"]"直接去掉。這樣就可以將"target[0]"變?yōu)?target.0"。具體代碼如下:
function get(data, ...args) { return args.map((item) => { let res = data; item .replace(/[/g, ".") .replace(/]/g, "") .split(".") .map(path => res = res && res[path]); return res; }) }
而且這兩種方式的好處在于,它也可以處理多維數(shù)組的情況。
總結(jié)學習完之后,最重要就是要總結(jié),只有總結(jié)下來了,知識才是你自己的。那么我來總結(jié)下文章想表達的內(nèi)容:
對于具有固定格式的字符串,可以考慮使用正則表達式來識別和匹配。
實現(xiàn)一個功能的時候,不要只考慮正常情況,要多考慮一些非正常情況,比如輸入格式不對、用戶不按套路來或者因為一些奇奇怪怪的事情報錯。并且能對可預見的非正常情況做一個容錯處理。
有時候還是可以多學習了解一下一些黑科技(比如Function),說不定哪天就可以用它來解決問題。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/40215.html
摘要:這個時候查了下正則表達式的文檔文檔點擊這里,發(fā)現(xiàn)有一個方法,可以返回匹配成功的結(jié)果。那么我來總結(jié)下文章想表達的內(nèi)容對于具有固定格式的字符串,可以考慮使用正則表達式來識別和匹配。 今天在認真干(劃)活(水)的時候,看到群里有人發(fā)了一道頭條的面試題,就順便看了一下,發(fā)現(xiàn)挺有意思的,就決定分享給大家,并且給出我的解決方案和思考過程。 題目如下: 實現(xiàn)一個get函數(shù),使得下面的調(diào)用可以輸出正確...
摘要:這個時候查了下正則表達式的文檔文檔點擊這里,發(fā)現(xiàn)有一個方法,可以返回匹配成功的結(jié)果。那么我來總結(jié)下文章想表達的內(nèi)容對于具有固定格式的字符串,可以考慮使用正則表達式來識別和匹配。 今天在認真干(劃)活(水)的時候,看到群里有人發(fā)了一道頭條的面試題,就順便看了一下,發(fā)現(xiàn)挺有意思的,就決定分享給大家,并且給出我的解決方案和思考過程。 題目如下: 實現(xiàn)一個get函數(shù),使得下面的調(diào)用可以輸出正確...
摘要:重溫一個面試題內(nèi)容數(shù)組內(nèi)容為數(shù)組內(nèi)容為個英文字母,使用兩個線程分別輸入兩個數(shù)組,打印內(nèi)容為這樣的規(guī)律提取一下核心內(nèi)容,去除次要內(nèi)容兩個線程需要交替執(zhí)行,打印數(shù)字的線程需要先執(zhí)行,數(shù)組打印完畢后線程需要結(jié)束。 一道多線程面試題引起的自我救贖 近日去一個知名互聯(lián)網(wǎng)企業(yè)參加面試,之前準備多多信心滿滿,但是面試一開始就是一道不起眼的編程題 數(shù)組A內(nèi)容為 1,2,3,4...52 ,數(shù)組B內(nèi)容...
摘要:一篇文章和一道面試題最近,有篇名為張圖幫你一步步看清和的執(zhí)行順序的文章引起了我的關(guān)注。作者用一道年今日頭條的前端面試題為引子,分步講解了最終結(jié)果的執(zhí)行原因。從字面意思理解,讓我們等等。當前的最新版本,在這里的執(zhí)行順序上,的確存在有問題。 一篇文章和一道面試題 最近,有篇名為 《8張圖幫你一步步看清 async/await 和 promise 的執(zhí)行順序》 的文章引起了我的關(guān)注。 作者用...
閱讀 2821·2023-04-25 18:46
閱讀 711·2021-11-19 09:40
閱讀 2077·2021-09-28 09:36
閱讀 3384·2021-09-10 11:11
閱讀 3464·2019-08-30 15:55
閱讀 1803·2019-08-30 15:54
閱讀 2598·2019-08-29 16:16
閱讀 3542·2019-08-29 15:08