相信很多人看到这个题就觉得太easy
了,不就是解析url参数
吗?split一把梭
不就行了?别急 往下看。
现在有如下的一个url,请你解析出GET参数
let url = "http://www.xxx.com?a=1&b=2&c=3"
// 也就是获取到: {a: 1, b: 2, c: 3}
你可能瞬间写出来:
最基础版本
split一把梭
function parse(url) {
let obj = {};
url
.slice(url.indexOf("?") + 1)
.split("&")
.map(item => {
let [key, val] = item.split("=");
obj[key] = val;
});
return obj;
}
let result = parse(url);
console.log(result);
// { a: '1', b: '2', c: '3' }
ok, 这是作为一名前端必须掌握的最基础的知识点,能写出来不代表你多厉害,但是如果上面这个方法你写不出来,去面壁思过吧?
接着往下继续深问:
上面使用map
,你可以用reduce
实现吗?能写出来说明你的基础还算可以。
基础版本
reduce
function parse(url) {
return url
.slice(url.indexOf("?") + 1)
.split("&")
.reduce((prev, curr) => {
let [key, val] = curr.split("=");
prev[key] = val;
return prev;
}, {});
}
let result = parse(url);
console.log(result);
// { a: '1', b: '2', c: '3' }
ok,上方都是基础的开胃菜,下方正文开始
坑1 没有value
如果参数长这样子怎么搞?
let url = "http://www.xxx.com?a&b&c"
如果用上方方法解析会返回如下:
{ a: undefined, b: undefined, c: undefined, d: undefined }
不难,加个val的判断即可
function parse(url) {
return url
.slice(url.indexOf("?") + 1)
.split("&")
.reduce((prev, curr) => {
let [key, val] = curr.split("=");
if (!val) return prev; // val 为空情况
prev[key] = val;
return prev;
}, {});
}
let result = parse(url);
console.log(result);
// {}
坑2 对象的querystring
如果参数长这样子怎么搞?
let url = "http://www.xxx.com?a[name]=tiger&a[age]=25&b[name]=cat&c=666"
这个就比较恶心了,单独将这一坨给拎出来:a[name]=tiger&a[age]=25&b[name]=cat&c=666
其实就是解析成如下的一个对象:
{
a:{
name:'tiger',
age:25
},
b:{
name:'cat'
},
c:666
}
如果还用上方方法会解析成如下:{ 'a[name]': 'tiger', 'a[age]': '25', 'b[name]': 'cat', c: '666' }
明显不是我们想要的,怎么搞?其实也不难,增加个方法去深度判断一级key还是二级key
(这里将a和b理解为二级key,c为一级key
),然后赋值即可。
function parse(url) {
return url
.slice(url.indexOf("?") + 1)
.split("&")
.reduce((prev, curr) => {
let [key, val] = curr.split("=");
if (!val) return prev;
// 解析 'a[name]' -> [a, name]
let path = key.split(/[\[\]]/g).filter(x => x);
deep_parse(
prev, // {}
path, // [a, name]
val // tiger
);
return prev;
}, {});
}
function deep_parse(obj, path, val) {
let i = 0;
let deep_obj = {};
// 处理二级key 只有二级key情况才会进入for循环 i会等于1
for (; i < path.length - 1; i++) {
if (!obj[path[i]]) {
obj[path[i]] = {};
}
deep_obj = obj[path[i]];
}
if (i === 0) {
// 一级key情况 c
obj[path[i]] = val;
} else {
// 二级key情况 a b
deep_obj[path[i]] = val;
}
}
let result = parse(url);
console.log(result);
// { a: { name: 'tiger', age: '25' }, b: { name: 'cat' }, c: '666' }
此方法有两处可以替换或改进
的地方。
1 . path解析
let path = key.split(/[\[\]]/g).filter(x => x)
可以替换成:
let path = key
.replace("[", "]")
.split("]")
.filter(x => x)
一样的都是将'a[name]' 解析为 [a, name]
2 .deep_parse方法
中的deep_obj
有必要创建吗?
其实是没必要的,我们知道JS中的对象是引用类型
。新创建一个内存地址来引用其他对象
和直接用原对象obj的引用
没有区别,改变的仍是原对象obj
。 所以deep_parse方法
也可以这样写:
function deep_parse(obj, path, val) {
let i = 0;
// 处理二级key 只有二级key情况才会进入for循环 i会等于1
for (; i < path.length - 1; i++) {
if (!obj[path[i]]) {
obj[path[i]] = {};
}
obj = obj[path[i]];
}
// 一级key情况 c + 二级key情况 a b
obj[path[i]] = val;
}
坑3 数组的querystring
如果参数长这样子怎么搞?
let url = "http://www.xxx.com?a[0]=1&a[1]=2&b[0]=3&c=4"
单独拎出来:a[0]=1&a[1]=2&b[0]=3&c=4
其实就是解析成如下的一个对象,其中a和b是数组:
{ a: [ '1', '2' ], b: [ '3' ], c: '4' }
如果还用上方方法会解析成如下:{ a: { '0': '1', '1': '2' }, b: { '0': '3' }, c: '4' }
貌似不太对啊,数组的下标变成了对象的key
。思考一个问题: 对象有key,但是数组有key吗?数组没有key 但是数组有索引啊。那将数组替换成对象
,再把对象的key变成数组的索引
不就行了?
function parse(url) {
return url
.slice(url.indexOf("?") + 1)
.split("&")
.reduce((prev, curr) => {
let [key, val] = curr.split("=");
if (!val) return prev;
// 解析 'a[name]' -> [a, name]
let path = key.split(/[\[\]]/g).filter(x => x);
deep_parse(
prev, // {}
path, // [a, name]
val // tiger
);
return prev;
}, {});
}
function deep_parse(obj, path, val) {
let i = 0;
// 处理二级key 只有二级key情况才会进入for循环 i会等于1
for (; i < path.length - 1; i++) {
if (!obj[path[i]]) {
// 判断第二个 key 是否是数字
if (path[i + 1].match(/^\d+$/)) {
obj[path[i]] = [];
} else {
obj[path[i]] = {};
}
}
obj = obj[path[i]];
}
obj[path[i]] = val;
}
let result = parse(url);
console.log(result);
// { a: [ '1', '2' ], b: [ '3' ], c: '4' }
可以看到只是稍稍改变了下deep_parse
方法for循环中的if判断条件
。如果第二个key是数字
,那么第一个key的val就是数组类型
即可。
坑4 空格要解码
如果参数长这样子怎么搞?
let url = "http://www.xxx.com?name=funky%20tiger"
也就是要对空格(%20)
进行解码。比较简单,直接decodeURIComponent
// 和上方代码一样
// ...
function deep_parse(obj, path, val) {
let i = 0;
for (; i < path.length - 1; i++) {
if (!obj[path[i]]) {
if (path[i + 1].match(/^\d+$/)) {
obj[path[i]] = [];
} else {
obj[path[i]] = {};
}
}
obj = obj[path[i]];
}
// decodeURIComponent解码
obj[path[i]] = decodeURIComponent(val);
}
let result = parse(url);
console.log(result);
// { name: 'funky tiger' }
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。