如何手写 query string 的解析和序列化?
简化版
query string 解析是把 ?a=1&b=2 转成对象,序列化是把对象转回 URL 参数。核心步骤是去掉开头 ?,按 & 拆键值对,再用 decodeURIComponent 解码;序列化时跳过 undefined,使用 encodeURIComponent 编码。边界包括重复 key、数组、空值、中文、特殊字符和 + 是否代表空格。
详细版
浏览器已有 URLSearchParams,但手写能考察字符串处理和编码意识。
| 输入 | 输出 |
|---|---|
?a=1&b=2 | { a: "1", b: "2" } |
a=1&a=2 | { a: ["1", "2"] } |
q=前端 | { q: "前端" } |
function parseQuery(query: string) {
const res: Record<string, any> = {}
query.replace(/^\?/, '').split('&').filter(Boolean).forEach(pair => {
const [rawKey, rawValue = ''] = pair.split('=')
const key = decodeURIComponent(rawKey)
const value = decodeURIComponent(rawValue.replace(/\+/g, ' '))
if (res[key] === undefined) res[key] = value
else res[key] = Array.isArray(res[key]) ? [...res[key], value] : [res[key], value]
})
return res
}
query 处理的核心不是 split,而是编码、重复 key 和空值语义。
完整版教学
一、题目考察点
这题常见于工具函数手写,考察字符串拆分、URL 编码、数组处理和边界意识。
实际项目中推荐使用 URLSearchParams 或成熟库,但面试手写要能说明简化版和工程版差异。
二、解析基础流程
解析流程是:
- 去掉开头的
?。 - 按
&拆成键值对。 - 按第一个
=拆 key 和 value。 - 解码 key/value。
- 放入结果对象。
function decode(value: string) {
return decodeURIComponent(value.replace(/\+/g, ' '))
}
+ 在表单编码中常表示空格,是否处理要按约定说明。
三、重复 key 怎么处理
URL 允许重复 key,例如 tag=js&tag=css。常见处理方式有三种:
| 方式 | 结果 | 说明 |
|---|---|---|
| 后者覆盖前者 | { tag: "css" } | 简单但丢信息 |
| 转数组 | { tag: ["js", "css"] } | 更完整 |
| 始终数组 | { tag: ["js"] } | 统一但使用麻烦 |
手写时转数组比较稳。
四、序列化实现
序列化要把对象变成 a=1&b=2。
function stringifyQuery(obj: Record<string, any>) {
const parts: string[] = []
Object.keys(obj).forEach(key => {
const value = obj[key]
if (value === undefined) return
const values = Array.isArray(value) ? value : [value]
values.forEach(item => {
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(item ?? '')}`)
})
})
return parts.join('&')
}
null 是否序列化为空字符串,也要看项目约定。
五、等号和值里的特殊字符
如果 value 里本身有 =,简单 split('=') 会丢内容。更稳的做法是找第一个等号下标。
const index = pair.indexOf('=')
const key = index >= 0 ? pair.slice(0, index) : pair
const value = index >= 0 ? pair.slice(index + 1) : ''
六、和 URLSearchParams 的区别
URLSearchParams 已经处理了很多标准细节。
const params = new URLSearchParams(location.search)
params.getAll('tag')
但它转普通对象时仍要你决定重复 key、数组和空值如何表达。
七、常见误区与追问
- 误区:query 只要按
&和=split。 编码、重复 key、空值和 value 中的等号都要考虑。 - 误区:不用 encode/decode 也能跑。 中文、空格、
&、=等特殊字符会出问题。 - 误区:重复 key 一定覆盖。 表单和筛选条件中重复 key 很常见,转数组更完整。
- 追问:
+要不要转空格? 表单编码中常需要,严格 URL 百分号编码场景要按约定。 - 追问:undefined 和 null 怎么处理? 常见做法是跳过 undefined,null 转空字符串或字符串
"null"需约定。 - 追问:为什么推荐 URLSearchParams? 标准 API 更可靠,手写主要用于理解和特殊格式适配。
八、加强记忆
query 手写记成“拆、解码、合并;遍历、编码、拼接”。真正的坑在重复 key、特殊字符、空值和标准 API 的取舍。