当你看到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