这是一到非常典型的面试题,潜意识里他没什么难的,但今天看到一位大神写出来的代码卓识有点水平,拿出来分析了一下,才有些领会为什么这道看似简单的面试题会经常的出现,包括一些大厂也经常问到,确实能提现资深的开发者和初级开发者的水准。
题:写一个程序,解析下面的queryString,返回对象
初级
function parse(str) {
return str.split('&').reduce((o, kv) => {
const [key, value] = kv.split("=");
if(!value) {
return o;
}
o[key] = value
return o
}, {})
}
let url = https://www.juejin.cn?name=js&age=20&phone=123456let
let urlObj = parse(url)
如果在面试中三五分钟写出这段代码也还不错,能实现基本的要求,并且用reduce函数处理代码非常整洁,接着往下。
高级
上边的代码会有个坑,如果url是这样的:
https://juejin.cn?name=js&age=20&company[name]=xxx&company[address]=ooo
https://juejin.cn?a[name]=js&a[age]=20&a[company][name]=xxx&a[company][address]=ooo
代码:
function parse(str){
return str.split('&').reduce((o, kv) => {
const [key, value] = kv.split("=");
if(!value) {
return o;
}
deep_set(o, key.split(/[\[\]]/g).filter(x=>x), value)
return o
}, {})
}
function deep_set(o, path, value){
let i = 0;
for(; i < path.length -1; i++) {
if(o[path[i]] === undefined) {
o[path[i]] = {}
}
o = o[path[i]]
}
o[path[i]] = value;
};
let url = https://www.juejin.cn?name=js&age=20&company[name]=xxx&company[address]=ooo;
let urlObj =parse(url)
这几行代码写出来以后就显得有点水平了,但是笔者第一次看 deep_set函数的时候有点懵逼,让我们来一起解析一下。
function deep_set(o, path, value) { //首先接受形参o
let i = 0;
// path 是属性数组,循环次数length-1,最后项不循环
for(; i < path.length -1; i++) {
if(o[path[i]] === undefined) {
// 如果o[path[i]] 为 undefined 赋值 {}
o[path[i]] = {}
}
//又赋值回去给o(最不理解的是这行,赋值给o以后o不就变了嘛,原来的o不成了最新的了)
o = o[path[i]]
}
o[path[i]] = value //循环完以后再赋值
}
理解这段代码 要熟悉引用类型,还有函数赋值
// 知识点1:函数接收参数,本质上的执行赋值的过程
deep_set(o, key.split(/[[]]/g).filter(x=>x), value)
function deep_set(o, path, value) {
// 实际上是执行了 o = o
}
// 知识点2:引用类型赋值,浅拷贝
描述太复杂举个例子:
var o = {a: 1, b: {c:2, d:3}, f: 5}
var a = o; // a指向o
a = o['b']; // a指向了o.b
a['e'] = {}; // 给a添加属性e,相当于 o.b 添加了一个属性e
a = a['e']; // 又把a指向了e
a['g'] = 8; // 给a添加一个属性g并且赋值8,相当于给对象e添加了一个属性g=8
console.log(a); // { g: 8 }
console.log(o); // { a: 1, b: { c: 2, d: 3, e: { g: 8 } }, f: 5 }
// 最终o的结果: { a: 1, b: { c: 2, d: 3, e: { g: 8 } }, f: 5 }
理解上述解析过程再回头看看代码是不是非常容易理解了,但是笔者认为原作者没有考虑到代码的可读性,所有我们换一下变量更能理解,如下:
function parse(str){
return str.split('&').reduce((o, kv) => {
const [key, value] = kv.split("=");
if(!value) {
return o;
}
deep_set(o, key.split(/[[]]/g).filter(x=>x), value)
return o
}, {})
}
function deep_set(o, path, value) {
let a = o;
let i = 0;
for(; i < path.length -1; i++) {
if(a[path[i]] === undefined) {
a[path[i]] = {}
}
a = a[path[i]]
}
a[path[i]] = value
}
// 似乎这样的可读性高一些
还没完接着向下
大神
上边代码还有个坑,如果地址栏传出的是一个数组怎么办,例如下面 url里面的a
let url=juejin.cn?name=js&age=20&company[name]=xxx&company[address]=ooo&a[0]=1&a[1]=2;
// 代码
function parse(str){
return str.split('&').reduce((o, kv) => {
const [key, value] = kv.split("=");
if(!value) {
return o;
}
deep_set(o, key.split(/[[]]/g).filter(x=>x), value)
return o
}, {})
}
function deep_set(o, path, value) {
let a = o;
let i = 0;
for(; i < path.length -1; i++) {
if(a[path[i]] === undefined) {
if(path[i+1].match(/^\d+$/)) { // 判断是否是数组
a[path[i]] = []
} else {
a[path[i]] = {}
}
}
a = a[path[i]]
}
a[path[i]] = value
}
let urlObj =parse(url)
结束