JavaScript 获取 URL 参数:重复键、中文解码与缺省值怎么处理

编程狮(w3cschool.cn) 2026-09-11 17:52:25 浏览数 (37)
反馈

获取 URL 参数优先用 URLSearchParams;重复键用 getAll,完整地址先交给 URL。你还要单独处理“参数不存在”和“参数存在但为空”,否则分页、筛选和分享链接容易出现难以复现的错误。

URL 参数怎么读别把片段当查询串

假设你正在做课程搜索页,地址中有关键词、页码和多个分类。英文搜索正常,中文偶尔乱码;清空关键词以后页面却沿用旧条件。这类问题常来自手动拆字符串,或者把所有空值都当成同一个默认值。本文围绕同一个筛选地址,给出三种读取场景、类型转换规则以及可运行断言。编程狮使用现代浏览器与支持 URL API 的 Node.js 说明,不要求你安装额外解析库。

一、获取 URL 参数时,先分清查询部分和片段

URL 可以想成写在信封上的一整套地址,查询参数只占其中一段。问号后、井号前的部分才是常见的 search;井号后的 hash 通常由浏览器或前端路由解释,并不会作为同样的查询部分发送给服务器。

因此,不要对整个地址直接 split("&")。这样会把路径、片段里的问号以及参数值中的编码混在一起。先交给 URL 拆结构,再交给 URLSearchParams 处理键值,两层各自负责一个问题。

本文使用的样例包含 page=2、重复的 tag,以及空的 q。为了避免让结果依赖你当前打开的网页,所有可执行代码都使用显式地址,而不是直接读取全局 location。上线接入时,浏览器页面才把这个地址替换为 location.href。这样的写法同样便于写测试:同一条输入在不同页面都产生同一结果。

const url = new URL("https://example.test/courses?page=2&tag=js&tag=web&q=#intro");
console.log(url.pathname); // /courses
console.log(url.searchParams.get("page")); // "2"
console.log(url.hash); // #intro

这段程序不访问网络,只在本地解析字符串。因此,即使示例域名不可访问,你仍然能验证结果。

二、单值、重复值和缺失值,分别调用对应方法

get 返回第一个匹配值,getAll 返回所有匹配值。has 用于判断键是否存在,这与判断值是否为真是两回事。下面的断言可以直接放进浏览器控制台或 Node.js:

const params = new URLSearchParams("tag=js&tag=web&q=&page=0");
console.assert(params.get("tag") === "js");
console.assert(params.getAll("tag").join(",") === "js,web");
console.assert(params.has("q") === true);
console.assert(params.get("q") === "");
console.assert(params.get("missing") === null);
console.assert(params.get("page") === "0");

参数缺失得到 null,显式空值得到空字符串,而数字外观的页码依然是字符串。直接使用“值或默认值”的写法,会把空字符串也替换掉;使用空值合并只会处理 null 与 undefined。哪个正确,要看页面是否允许用户明确清空条件。

如果分类可多选,就保留数组直到请求接口,不要先调用 get 再期望拿到全部分类。反过来,如果 page 只能有一个,遇到重复 page 应明确选择首个、末个或拒绝请求。不同端对重复参数处理不一致,会产生“页面显示第二页,接口实际请求第一页”的问题。JavaScript 的基本类型与判断规则可配合 JavaScript 教程 阅读。

三、三种输入场景:查询字符串、完整地址与哈希路由

只有查询字符串时直接构造 URLSearchParams;拿到完整地址时先构造 URL;使用把参数放进 hash 的旧式路由时,才提取片段中的查询部分。这三种写法解决的是不同输入形态,并不是谁比谁更高级。

const fromSearch = new URLSearchParams("?q=javascript");
console.assert(fromSearch.get("q") === "javascript");

const fromAddress = new URL("/courses?page=3", "https://example.test");
console.assert(fromAddress.searchParams.get("page") === "3");

const routeUrl = new URL("https://example.test/#/courses?q=vue");
const mark = routeUrl.hash.indexOf("?");
const routeParams = new URLSearchParams(
  mark < 0 ? "" : routeUrl.hash.slice(mark + 1)
);
console.assert(routeParams.get("q") === "vue");

相对地址必须提供 base,否则 URL 无法判断主机和路径。哈希示例只适用于片段内部以问号承载查询串的路由约定,不应该无条件应用到所有网站。项目已经使用路由库时,应优先读取路由库解析好的 query,避免两套解析器给出不同结果。

另一个常见误用,是直接把完整 https 地址传给 URLSearchParams。它不会自动替你提取 search,而会把地址当作参数字符串的一部分。排查时先打印输入的类型和结构,确认你传进去的是整个地址还是问号后的内容。

四、中文解码与页码校验,要放在不同步骤

URLSearchParams 已经执行参数解码,不需要再对读取结果调用 decodeURIComponent。重复解码可能改变用户原本输入的百分号编码,遇到残缺的百分号序列还可能抛错。查询字符串中的加号通常代表空格;真实的加号应编码成 %2B。

const encoded = new URLSearchParams();
encoded.set("q", "C++ 中文");
console.log(encoded.toString()); // q=C%2B%2B+%E4%B8%AD%E6%96%87
console.assert(new URLSearchParams(encoded.toString()).get("q") === "C++ 中文");

function readPage(raw) {
  if (raw === null || !/^[1-9]\d*$/.test(raw)) return 1;
  const value = Number(raw);
  return Number.isSafeInteger(value) && value <= 1000 ? value : 1;
}
console.assert(readPage("2") === 2);
console.assert(readPage("2abc") === 1);
console.assert(readPage("") === 1);

示例把页码限定为一到一千的正整数,超出范围回退第一页。这是演示业务约定,不是 URL API 自带限制。使用 parseInt 时,“2abc”可能被读成 2,所以只转换数字并不等于校验输入。价格范围、布尔开关、日期都应该有各自的验证函数。

前端安全也不能依赖参数来源。把关键词展示到页面时使用 textContent 或框架默认文本插值;不要把 URL 参数直接作为 HTML 注入。编码解决传输表达,校验解决业务格式,安全输出解决展示边界,三个步骤不能互相替代。需要复习输入框与页面文本承载方式时,可以查看 HTML 教程

观察到的结果 含义 建议处理
null 键不存在 使用默认筛选
空字符串 键存在但值为空 按清空操作处理
多个 tag 重复键 用数组保留
加号变空格 表单式解码规则 用构造器编码真实加号

同一个地址,三个区域各有职责

总结

获取 URL 参数分成三个动作:根据输入形态选解析入口,按键的数量读取结果,再根据业务规则转换类型。查询串、完整地址和 hash 路由必须区别处理,尤其不要把完整地址直接交给 URLSearchParams。

落地时为中文、重复键、空值、缺失值和非法页码各留一个断言。以后增加分类筛选或分享链接,只需补充参数契约,不必重新写一套拆字符串逻辑。生成链接时也使用同一套 API,让写入与读取互相验证,能减少大量只在特殊关键词下出现的问题。

延伸学习

  1. 用 [JavaScript 参考手册_blank](https://www.w3cschool.cn/jsref/ 对照字符串和数值转换的边界。
  2. 通过 JavaScript 基础实战 继续练习页面数据处理。
  3. 阅读 JavaScript 控制台入门指南,练习逐项检查参数结果。

常见问题

Q:为什么获取到的 true 不是布尔值?

A:查询参数读取结果是字符串。应按约定判断它是否等于 "true",而不是直接用 Boolean 转换;非空字符串 "false" 转换后也是真。

Q:可以用 Object.fromEntries 一次转成对象吗?

A:可以,但普通对象不能保留同名键的全部值,重复键会被覆盖。多选分类应使用 getAll,或显式构建数组,避免在转换时丢失信息。

Q:解析一个外部地址会发请求吗?

A:不会。URL 和 URLSearchParams 在这里处理的都是字符串。只有你把结果交给 fetch、导航或其他网络功能,才会发生相应访问。

0 人点赞