今天在認真幹(劃)活(水)的時候,看到群裡有人發了一道頭條的面試題,就順便看了一下,發現挺有意思的,就決定分享給大家,並且給出我的解決方案和思考過程。
題目如下:
實現乙個get函式,使得下面的呼叫可以輸出正確的結果
const obj = }, target: [1, 2, ]};
get(obj, 'selector.to.toutiao', 'target[0]', 'target[2].name');
// [ 'fe coder', 1, 'byted']
乍眼一看,這不就是實現乙個lodash.get方法嗎?看上去好像很簡單。所以我就開始寫了第乙個版本。思想其實很簡單,遍歷傳進來的引數,使用split將每乙個引數分隔開,然後遍歷取值,最終返回結果。
function get(data, ...args) )
}
一執行,果不其然,報錯了。
後來仔細看了一下提供的測試**,發現居然有target[0]這種東西。。居然還帶了個陣列索引。
冷靜分析一下,對於後面帶了個索引的型別,比如'target[0]',我們肯定是要特殊對待的。所以,我們首先得先識別到這種特殊的型別,然後再對它進行額外處理。
這個時候,很快的就可以想到使用正規表示式來做這個事情。為什麼呢?因為像這種帶有索引的型別,他們都有乙個特色,就是有固定的格式:[num],那麼我們只需要能構造出可以匹配這種固定格式的正則,就可以解決這個問題。
對於這種格式,不難想到可以用這個正規表示式來做判斷:/\[[0-9]+\]/gi,可是我們還需要將匹配值取出來。這個時候查了下正規表示式的文件(文件點選這裡),發現有乙個match方法,可以返回匹配成功的結果。那麼就讓我們來做個測試:
const reg = /\[[0-9]+\]/gi;
const str = "target[123123]";
const str1 = "target"
if (reg.test(str))
if (!reg.test(str1))
const matchresult = str.match(reg);
console.log(matchresult); // ["[123123]"]
誒,我們現在已經找到了解決這種問題的方法,那讓我們趕緊來繼續改進下**。
function get(data, ...args) else
});return res;
});}const obj = }, target: [1, 2, ]};
console.log(get(obj, 'selector.to.toutiao', 'target[0]', 'target[2].name'));
寫完趕緊執行一下,完美,輸出了正確的結果了。那麼到這裡就結束了?
可是總感覺有點不妥,感覺事情沒有那麼簡單。一般來說,面試題除了考驗你解決問題的能力之外,可能還考驗著你思考問題的全面性、嚴謹性。像上面那種寫法,如果使用者傳入了乙個不存在的path鏈或者一些其他特殊情況,就可能導致整個程式crash掉。想下lodash.get呼叫方式,即使你傳入了錯誤的path,他也可以幫你做處理,並且返回乙個undefined。因此,我們還需要完善這個方法。
function get(data, ...args) else
} catch (err)
});return res;
});}
在這裡,我們對每乙個path的處理進行了try catch處理。若出錯了,則返回undefined。哇,這樣看起來就比較穩了。
那麼,有沒有別的解決方法呢?
群裡有乙個大佬提出了一種更簡單也很取巧的解決方案,就是通過構建乙個function解決這個問題(function的詳細介紹點選這裡)。由於**很簡單,我就直接貼出來了:
function get(data, ...args) .$ } catch(e) {}`))());
}const obj = }, target: [1, 2, ]};
console.log(get(obj, 'selector.to.toutiao', 'target[0]', 'target[2].name', 'asd'));
看完之後,就兩個字,牛逼。
這種方法我承認一開始我確實沒想到,確實是很奇技淫巧。不過仔細思考了下,其實很多框架都用到了這個奇技淫巧。比如說vue裡,就使用new function的方式來動態建立函式,解決執行動態生成的**的問題。
再比如說,function.prototype.bind方法裡(我寫了個類似的bind方法:倉庫),也使用了function來解決一些問題(fn.length丟失問題)。說明這個東西還是挺有用的,得學習了解一波,說不定哪天就用到了。
更新
有人提到了那種function的方式沒辦法處理以下的處理:
let obj = ;
function get(data, ...args) } catch(e) {}`))(data));
}
除此之外, **宇宙提出了另一種解決方案,就是將"target[0]"分為兩個key,也很簡單粗暴,就是將在split之前,將字串裡的'['替換為'.',將']'直接去掉。這樣就可以將"target[0]"變為"target.0"。具體**如下:
function get(data, ...args) )
}
而且這兩種方式的好處在於,它也可以處理多維陣列的情況。
對於具有固定格式的字串,可以考慮使用正規表示式來識別和匹配。
實現乙個功能的時候,不要只考慮正常情況,要多考慮一些非正常情況,比如輸入格式不對、使用者不按套路來或者因為一些奇奇怪怪的事情報錯。並且能對可預見的非正常情況做乙個容錯處理。
有時候還是可以多學習了解一下一些黑科技(比如function),說不定哪天就可以用它來解決問題。
一道面試題的思考
在繼承中new和override相同點和區別?看下面的 有乙個基類a,b1和b2都繼承自a,並且使用不同的方式改變了父類方法print 的行為。測試 輸出什麼?為什麼?public void dotest public class a public class b1 a public class b...
一道面試題引發的思考
首先我們給出這道面試題的 以及題目 lista new arraylist list.add 1 list.add 2 for string item list 問 上段 執行會報錯嗎?如果把 1 換成 2 會報錯嗎?為什麼?首先給出答案 上面這段 執行不會報錯。把 1 換成 2 再執行就會報錯。為...
一道C面試題的思考
c語言真的是學無止境的感覺,大部分同學大學都會開設c語言課程。很多人把c語言二級過了就感覺入門了 對於那些在做嵌入式開發的工程師,幾乎每天都要接觸c語言,很多人會感覺自己c語言學得很溜了。那好,咱們用一道c語言面試題來測試一下。首先給出題目 定義乙個巨集,求兩個數中的最大數 ok,很多人應該能很快寫...