摘要:这个时候查了下正则表达式的文档文档点击这里,发现有一个方法,可以返回匹配成功的结果。那么我来总结下文章想表达的内容对于具有固定格式的字符串,可以考虑使用正则表达式来识别和匹配。
今天在认真干(划)活(水)的时候,看到群里有人发了一道头条的面试题,就顺便看了一下,发现挺有意思的,就决定分享给大家,并且给出我的解决方案和思考过程。
题目如下:
实现一个get函数,使得下面的调用可以输出正确的结果
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"]
乍眼一看,这不就是实现一个lodash.get方法吗?看上去好像很简单。所以我就开始写了第一个版本。思想其实很简单,遍历传进来的参数,使用split将每一个参数分隔开,然后遍历取值,最终返回结果。
function get(data, ...args) { return args.map((item) => { const paths = item.split("."); let res = data; paths.map(path => res = res[path]); return res; }) }
一运行,果不其然,报错了。
后来仔细看了一下提供的测试代码,发现居然有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)) { console.log("test success"); } if (!reg.test(str1)) { console.log("test fail"); } const matchResult = str.match(reg); console.log(matchResult); // ["[123123]"]
诶,我们现在已经找到了解决这种问题的方法,那让我们赶紧来继续改进下代码。
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, ""); // 获取数组索引 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"));
写完赶紧运行一下,完美,输出了正确的结果了。那么到这里就结束了?
改进可是总感觉有点不妥,感觉事情没有那么简单。一般来说,面试题除了考验你解决问题的能力之外,可能还考验着你思考问题的全面性、严谨性。像上面那种写法,如果用户传入了一个不存在的path链或者一些其他特殊情况,就可能导致整个程序crash掉。想下lodash.get调用方式,即使你传入了错误的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。哇,这样看起来就比较稳了。
那么,有没有别的解决方法呢?
群里有一个大佬提出了一种更简单也很取巧的解决方案,就是通过构建一个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的方式来动态创建函数,解决执行动态生成的代码的问题。
再比如说,Function.prototype.bind方法里(我写了个类似的bind方法:仓库),也使用了Function来解决一些问题(fn.length丢失问题)。说明这个东西还是挺有用的,得学习了解一波,说不定哪天就用到了。
更新
有人提到了那种Function的方式没办法处理以下的处理:
let obj = {time : new Date(), a : "this is a", b : 30};
因为JSON.stringfy后,Date、Function和RegExp类型的变量都会失效。对于这种情况,评论区有个大佬(冯恒智)也提到了一种很好的解决方案:
function get(data, ...args) { return args.map((item) => (new Function("data",`try {return data.${item} } catch(e) {}`))(data)); }
除此之外, 代码宇宙提出了另一种解决方案,就是将"target[0]"分为两个key,也很简单粗暴,就是将在split之前,将字符串里的"["替换为".",将"]"直接去掉。这样就可以将"target[0]"变为"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; }) }
而且这两种方式的好处在于,它也可以处理多维数组的情况。
总结学习完之后,最重要就是要总结,只有总结下来了,知识才是你自己的。那么我来总结下文章想表达的内容:
对于具有固定格式的字符串,可以考虑使用正则表达式来识别和匹配。
实现一个功能的时候,不要只考虑正常情况,要多考虑一些非正常情况,比如输入格式不对、用户不按套路来或者因为一些奇奇怪怪的事情报错。并且能对可预见的非正常情况做一个容错处理。
有时候还是可以多学习了解一下一些黑科技(比如Function),说不定哪天就可以用它来解决问题。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/99337.html
摘要:这个时候查了下正则表达式的文档文档点击这里,发现有一个方法,可以返回匹配成功的结果。那么我来总结下文章想表达的内容对于具有固定格式的字符串,可以考虑使用正则表达式来识别和匹配。 今天在认真干(划)活(水)的时候,看到群里有人发了一道头条的面试题,就顺便看了一下,发现挺有意思的,就决定分享给大家,并且给出我的解决方案和思考过程。 题目如下: 实现一个get函数,使得下面的调用可以输出正确...
摘要:这个时候查了下正则表达式的文档文档点击这里,发现有一个方法,可以返回匹配成功的结果。那么我来总结下文章想表达的内容对于具有固定格式的字符串,可以考虑使用正则表达式来识别和匹配。 今天在认真干(划)活(水)的时候,看到群里有人发了一道头条的面试题,就顺便看了一下,发现挺有意思的,就决定分享给大家,并且给出我的解决方案和思考过程。 题目如下: 实现一个get函数,使得下面的调用可以输出正确...
摘要:重温一个面试题内容数组内容为数组内容为个英文字母,使用两个线程分别输入两个数组,打印内容为这样的规律提取一下核心内容,去除次要内容两个线程需要交替执行,打印数字的线程需要先执行,数组打印完毕后线程需要结束。 一道多线程面试题引起的自我救赎 近日去一个知名互联网企业参加面试,之前准备多多信心满满,但是面试一开始就是一道不起眼的编程题 数组A内容为 1,2,3,4...52 ,数组B内容...
摘要:一篇文章和一道面试题最近,有篇名为张图帮你一步步看清和的执行顺序的文章引起了我的关注。作者用一道年今日头条的前端面试题为引子,分步讲解了最终结果的执行原因。从字面意思理解,让我们等等。当前的最新版本,在这里的执行顺序上,的确存在有问题。 一篇文章和一道面试题 最近,有篇名为 《8张图帮你一步步看清 async/await 和 promise 的执行顺序》 的文章引起了我的关注。 作者用...
阅读 2140·2023-04-25 18:49
阅读 1842·2019-08-30 14:02
阅读 2645·2019-08-29 17:24
阅读 3324·2019-08-28 18:10
阅读 2927·2019-08-28 18:03
阅读 490·2019-08-26 12:01
阅读 3310·2019-08-26 11:31
阅读 1412·2019-08-26 10:29