一道面试题引起的思考

更新日期: 2018-11-23阅读量: 1267标签: 面试

今天在认真干(划)活(水)的时候,看到群里有人发了一道头条的面试题,就顺便看了一下,发现挺有意思的,就决定分享给大家,并且给出我的解决方案和思考过程。题目如下:


实现一个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),说不定哪天就可以用它来解决问题。

本文地址在->本人博客地址, 欢迎给个 start 或 follow

原文来源:https://github.com/chenjigeng/blog/blob/master/一道面试题引起的思考.md


站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

链接: https://www.fly63.com/article/detial/1402

7个Js面试题及回答策略

不管你是面试官还是求职者,里面的思路都能让你获益匪浅。单体应用和微服务架构之间有哪些主要区别,从灵活性和可扩展性的角度来看,哪个是更好选择?

程序员经典面试题:高并发系统,一般需要怎么做

高并发系统总是那么令人着迷,比如说双十一的抢购,比如说12306的抢票,都是非常经典的高并发的例子,也是非常大的挑战。对于开发这样系统的人来说,最怕的就是突发的流量,就好比河流突发大水而引发洪涝灾害一样

每个 JavaScript 工程师都应当知道的 10 个面试题

多问问应聘者高层次的知识点,如果能讲清楚这些概念,就说明即使应聘者没怎么接触过 JavaScript,也能够在短短几个星期之内就把语言细节和语法之类的东西弄清楚。

程序员面试的时候,大家都中过什么套路?

利用面试人员解决难题,之前有网友爆料自己在面试一家创业公司的时候,最开始只是一位面试官在场,面着面着整个开发组的人都过来了,围绕面试者擅长的数据库开发,提出了非常具体的问题。

如何招聘一名优秀的前端人员?

招聘肯定要有标准,这样我们才能更快的找到我们需要的人才。在近期的大量面试中,我觉得有4个方面是必不可少的。代码能力强;经验丰富;性格合适;能融入团队

我是如何准备技术面试的

目前拿到的知名公司的offer是腾讯和新美大(大众点评、美团)。一直想写一篇面经分享给大家,但因为一些琐碎的事情,就一直耽误着。

大厂面试题分享:如何让(a===1&&a===2&&a===3)的值为true?

当我第一次看到这一题目的时候,我是比较震惊的,分析了下很不合我们编程的常理,并认为不大可能,变量a要在同一情况下要同时等于1,2和3这三个值,这是天方夜谭吧,不亚于哥德巴赫1+1=1的猜想吧,不过一切皆有可能

假如我是面试官,我会这样虐你

又是金三银四的时候,我希望这份面试题能够祝你一臂之力!技术方面从 Java 基础、JVM、Spring、数据库、拓展题等方面考察你,当然面试官可能还会问些其他的技术点。总的来说,还是得多准备充分,面试时灵活答辩

面试开发常用的 JavaScript 知识点总结

变量类型:var,定义变量;let,定义块域(scope)本地变量;const,定义只读常量。变量格式:以字母、下划线“_”或者$符号开头,大小写敏感。

大龄前端如何准备面试?

今年毋庸置疑是找工作的寒冬,今年出来找工作的每一个同学 都是值得尊敬的。 在寒冬季找工作,虽然略难,但是反过来看也会逼迫我们成为更加优秀的自己。但是不管是旺季还是寒冬,有一些优秀的同学找工作还是挺顺利的

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!