你会解析一个url参数吗?

1,314 阅读3分钟

在我们工作的过程中经常会遇到从网址url中传递参数的问题,当url中传递了参数,我们就需要从url中去解析这些参数。

举例: www.toutiao.com/search/?key…

这时我们需要从url中将keyword和其对应的值获取到,并应用到我们其他对应的代码逻辑中。

常规做法: 下面我们只针对于?后面的参数进行处理,也就是 keyword=fabu&name=bgcolor

function parse (str) {
    if (typeof str !== 'string') return;
    return str.split('&').reduce((acc, cur) => {
        var [key, value] = cur.split('=');
        // 如果不存在value,则不需要
        if (!value) return acc;
        acc[key] = value;
        return acc;
    }, {})
}
// 解析url
var url = 'keyword=fabu&name=bgcolor';
parse(url); // 得到的结果 {keyword: 'fabu', name: 'bgcolor'}

到这呢,基本上我们可以解析我们常见的url。

但是在实际开发过程中我们可能遇到的并不是这样的url,往往是相对复杂一点的url, 例如:

var str1 = 'a=1&b[name]=bgcolor&c[0]=c1&c[1]=c2&color=Deep%20color'

如果我们还是按照我们之前的写法去操作这个url的话,会得到什么呢?

parse(str1); 

得到的值:

{
    "a": "1",
    "b['name']": "bgcolor",
    "c[0]": "c1",
    "c[1]": "c2",
    "color": "Deep%20color"
}

而这个结果并不是我们想要的结果,我们希望的结果应该是

{
    "a": "1",
    "b": {
        "name": "bgcolor"
    },
    "c": [
        "c1",
        "c2"
    ],
    "color": "Deep color"
}

所以我们需要改造一下我们之前的parse函数

function parse (str) {
    if (typeof str !== 'string') return;
    return str.split('&').reduce((acc, cur) => {
        var [key, value] = cur.split('=');
        // 如果不存在value,则不需要
        if (!value) return acc;
        // acc[key] = value; //此处不能进行直接赋值操作了
        // 此时我们需要针对key,value进行一些操作处理,让他按照我们的预期结果返回,此时我们引入处理函数deep_set
        deep_set(acc, key, value);
        return acc;
    }, {})
}

// deep_set函数处理复杂key
function deep_set (o, path, value) {
    /*
     * 首先我们需要处理一下传的path
     * 将path中的[]通过正则转换为 . ,也就是将b[name]或c[0] ==> b.name , c.0
     * 将转换后的path分解为一个数组,并去除其中的空项
     * 然后我们开始循环一下当前转换后的path
    */
    var formatPath = path.replace(/[\[\]]/g, '.').split('.').filter(x => x);
    var lens = formatPath.length;
      /*
       * 遍历的时候由于formatPath的最后一项的值一定是一个原始数据类型,所以我们不需要处理这一项
       * 而其他的我们需要根据其属性赋予不同的数据类型
       * 例如b[name] ==> [b, name] i+1为string,所以是一个对象,我们赋值为{}
       * c[0] ==> [c, 0] i+1匹配数字,所以应该是一个数组,我们赋值 []
      */
      for (var i = 0; i < lens - 1; i++) {
        if (!o[formatPath[i]]) {
          o[formatPath[i]] = formatPath[i+1].match(/^\d+$/g) ? [] : {}
        }
        o = o[formatPath[i]];
      }
      o[formatPath[i]] = decodeURIComponent(value);
}

// 重新解析url
var str = 'a=1&b[name]=bgcolor&c[0]=c1&c[1]=c2&color=Deep%20color';
parse(str);

得到的结果:

{
  a: '1',
  b: { name: 'bgcolor' },
  c: [ 'c1', 'c2' ],
  color: 'Deep color'
}

其中如果传入deep_set的值没有对应的formatPath[i],我们则给其根据formatPath[i]的类型进行赋值空数组或是空对象。

然后再将 o重新赋值为o[formatPath[i]],直至所有的formatPath项都遍历完之后,我们将最后一项的进行赋值value。

第一次写博客,表达有点乱,不知道如何表达的更好,请各位大佬勿喷,如有问题可在评论中留言。