万万没想到,你是个套路满满的parseUrl

4,094 阅读5分钟

当你看到parseUrl的时候,首先映入眼帘的应该是曾几何时在面试的时候,或多或少都有被提及到的问题

我们不用太关心面试方面的东西,放轻松,不要带着心理压力,即使它是套路满满,我们也依然能轻松搞定

parseUrl主要的用途就是解析URL的查询字符串

那么,我们也不管什么hash写在#号后面的东东了,只回归到?号后面的查询字符串这里

一眼万年

遥想当年,当你看到这样的字符串'q=nba&src=home&fr=so',第一反应就是,小场面,用split一顿分割呗,先分割&,再分割=,小case的事

既然大家都会,我也就不卖关子了,开始撸一把啦

这个parseUrl很普通

写出一个普普通通的parseUrl,特别的normal,对于我们前端儿来说还是小菜一碟的

function parseUrl(str) {
    return str.split('&').reduce((obj, pair) => {
        // 解构数组所对应的值
        const [key, value] = pair.split('=');
        // 将键值对写入obj
        obj[key] = value;
        return obj;
    }, {});
}

// 测试用例1
let s1 = 'q=nba&src=home&fr=so';
console.log(parseUrl(s1));  // {q:'nba',src:'home',fr:'so'}

But,谁曾想,在你一顿操猛如虎的时候,其实已经埋下了一个小小的隐患

如果测试用例是'q=nba&src=home&fr=so&fe',就会出现小问题了,请看大屏幕

function parseUrl(str) {
    return str.split('&').reduce((obj, pair) => {
        const [key, value] = pair.split('=');
        obj[key] = value;
        return obj;
    }, {});
}

// 测试用例2
let s2 = 'q=nba&src=home&fr=so&fe';
console.log(parseUrl(s2));  // {q:'nba',src:'home',fr:'so',fe:undefined}

由于测试用例中的fe是没有value值的,所以直接就是unfefined,看似也不影响什么

不过这种情况能处理的话,我们就不能视而不见,下面来修改一下,非常的简单

没有value也得管

function parseUrl(str) {
    return str.split('&').reduce((obj, pair) => {
        const [key, value] = pair.split('=');
        ++++++
        // 没有value值的情况,就返回obj中已有的内容
        if (!value) {
            return obj;
        }
        ++++++
        obj[key] = value;
        return obj;
    }, {});
}

// 测试用例2
let s2 = 'q=nba&src=home&fr=so&fe';
console.log(parseUrl(s2));  // {q:'nba',src:'home',fr:'so'}

小小的隐患被我们轻易搞定了,我们再来看另外一种情况

URL的查询字符串是可以输入这样的字符的'q=nba&fe[pro]=news&fe[pid]=result'

按照上面的写法,打印出来是这样的{q:'nba','fe[pro]':'news','fe[pid]':'result'}

实际上,以我们熟知的知识体系来讲,明显能看出来fe其实是个对象类型,所以本不该是这样的,起码是这样的{q:'nba',fe:{pro:'news', pid:'result'}}

那么让我们来再研究研究吧

层层深入对象中

原理也是并不复杂,其实就是如果遇到这种看似对象类型的字符出现,我们就把它当成对象来处理,处理完第一层后,再处理第二层,以此类推下去

function parseUrl(str) {
    return str.split('&').reduce((obj, pair) => {
        const [key, value] = pair.split('=');
        if (!value) {
            return obj;
        }
        // obj[key] = value   废弃废弃废弃
        
        // 深入到obj的内部去搞
        deepObj(obj, key.split(/[\[\]]/g).filter(v => v), value);
        return obj;
    }, {});
}
// 深度设置对象
function deepObj(obj, keys, value) {
    // 下面我们所有涉及注释部分,都用fe[pro]来说明一下
    
    // fe[pro]被正则处理,匹配[和],以[或]分割出来的数组是这样的['fe', 'pro', '']
    // 然后我们只取非空值部分,就用filter过滤了一下,得到['fe', 'pro']
    console.log(keys);  // ['fe', 'pro']
    
    let i = 0;
    for (; i < keys.length - 1; i++) {
        let key = keys[i];
        // key值为fe
        if (!obj[key]) {    // obj中没有fe
            obj[key] = {};  // { fe: {} }
        }
        // 深入到obj的下一层
        obj = obj[key];     // 引用fe这个对象{}
    }
    // 相当于在fe对象里加属性,fe:{pro: 'news'}
    obj[keys[i]] = value;
}



// 测试用例3
let s3 = 'q=nba&fe[pro]=news&fe[pid]=result';
console.log(parseUrl(s3)); // {q:'nba',fe:{pro:'news', pid:'result'}}

上面的代码,通过注释的方式进行了简单分析,希望大家都可以理解

接下来,我们继续说一种与对象类似的,那就是写成数组的形式,且听风吟,来看这个用例'q=nba&box[0]=one&box[1]=two'

这样的写法对于解析来说,应该是返回如下格式{q:'nba',box:['one', 'two']}

那么,事不宜迟,我们再回到deepObj里看看如何处理吧

数组就该有你的样子

function parseUrl(str) {
    ...省略
}

function deepObj(obj, keys, value) {
    let i = 0;
    for (; i < keys.length - 1; i++) {
        let key = keys[i];
        if (!obj[key]) {
            ++++++
            // 如果是数组的话,keys应该是这样的['box', '0']
            // 所以来判断keys的i+1位置(就是索引为1),是不是数字就行
            // 是数字的话就当做数组类型来处理即可了
            if (keys[i + 1].match(/^\d+$/)) {
                obj[key] = [];
            } else {
                obj[key] = {};
            }
            ++++++
        }
        obj = obj[key]
    }
    obj[keys[i]] = value;
}


// 测试用例4
let s4 = 'q=nba&box[0]=one&box[1]=two';
console.log(parseUrl(s4));  // {q:'nba',box:['one', 'two']}

解码百分比编码

最后一种情况是这样的,'q=you%2Bme&name=jay%20chou',我们看到了%号,也就是说比如像空格,+号这样的特殊字符都被编码了

所以,别慌,既然被编码了,我们就可以利用提供好的解码方法搞定,它就是decodeURIComponent

代码也是非常好修改的,在deepObj函数中,只要将obj[keys[i]] = value改为obj[keys[i]] = decodeURIComponent(value)即可了,大功告成

function parseUrl(str) {
    ...省略
}
function deepObj(obj, keys, value) {
    ...省略
    obj[keys[i]] = decodeURIComponent(value);
}

// 测试用例5
let s5 = 'q=you%2Bme&name=jay%20chou';
console.log(parseUrl(s5));  // {q:'you+me',name:'jay chou'}

各显神通

通过上面的4种套路,其实已经能考到很多人了,没想到一个小小的解析查询字符串的操作,居然隐藏着如此神通

这也不得不让人唏嘘,JS的世界总是那么的博大精深,不过,我们还是不会停下脚步,继续努力学下去的

感谢大家的观看,886