URI 处理函数
URI(Uniform Resource Identifier,统一资源标识符)是用于标识资源(如网页、文件)和传输协议(如 HTTP、FTP)的字符串。ECMAScript 提供了四个用于编码和解码 URI 的内置函数,以及三个底层抽象操作。
一、编码函数
1.1 encodeURI(uri)
对完整 URI 进行编码,保留 URI 中的保留字符。
特点:
- 假设保留字符具有特殊含义(如作为分隔符),因此不进行编码
- 用于编码完整的 URI
保留字符(不编码): ;/?:@&=+$,#
encodeURI("https://example.com/path?name=张三")
// "https://example.com/path?name=%E5%BC%A0%E4%B8%89"1.2 encodeURIComponent(uriComponent)
对 URI 组件进行编码,编码所有保留字符。
特点:
- 假设保留字符只是普通文本,需要编码以避免在 URI 中产生特殊含义
- 用于编码 URI 的单个组成部分
保留字符(全部编码): 无(空字符串)
encodeURIComponent("张三")
// "%E5%BC%A0%E4%B8%89"
encodeURIComponent("name=value")
// "name%3Dvalue"二、解码函数
2.1 decodeURI(encodedURI)
对由 encodeURI 编码的 URI 进行解码。
特点:
- 保留
;/?:@&=+$,#这些字符的编码形式 - 不会解码非
encodeURI产生的编码
decodeURI("https://example.com/path?name=%E5%BC%A0%E4%B8%89")
// "https://example.com/path?name=张三"2.2 decodeURIComponent(encodedURIComponent)
对 URI 组件进行解码。
特点:
- 解码所有 percent 编码
- 用于解码由
encodeURIComponent编码的字符串
decodeURIComponent("%E5%BC%A0%E4%B8%89")
// "张三"三、底层抽象操作
3.1 Encode(string, extraUnescaped)
将字符串进行 URI 编码的抽象操作。
参数:
string: 要编码的字符串extraUnescaped: 额外的未转义字符
算法步骤:
- 定义
alwaysUnescaped: ASCII 字母数字字符 +- . ! ~ * ' ( ) - 合并
unescapedSet = alwaysUnescaped + extraUnescaped - 遍历字符串的每个码点:
- 如果字符在
unescapedSet中,不编码 - 否则,转换为 UTF-8 编码的字节序列,每个字节转为
%XX形式
- 如果字符在
- 如果遇到未配对的代理码,抛出
URIError
示例:
javascript
// encodeURI 内部调用 Encode(string, ";/?:@&=+$,#")
// encodeURIComponent 内部调用 Encode(string, "")
3.2 Decode(string, preserveEscapeSet)
将 URI 编码字符串解码的抽象操作。
参数:
string: 要解码的字符串preserveEscapeSet: 需要保留编码形式的字符集
算法步骤:
- 遍历字符串:
- 遇到普通字符,直接追加
- 遇到
%:- 读取后续两个十六进制字符
- 如果是 ASCII 字符且在
preserveEscapeSet中,保留%XX形式 - 否则,解码为对应字符
- 多字节 UTF-8 序列处理:
- 根据首字节的前导 1 位数量确定后续字节数
- 验证后续字节格式(必须以
%开头) - 将完整的 UTF-8 字节序列解码为 Unicode 码点
- 如果是无效 UTF-8,抛出
URIError
注意: RFC 3629 禁止解码无效的 UTF-8 序列,例如 0xC0 0x80 不能解码为 0x0000。
3.3 ParseHexOctet(string, position)
解析字符串指定位置的两个十六进制字符为无符号 8 位整数。
参数:
string: 输入字符串position: 起始位置
返回: 0-255 之间的整数,或 SyntaxError
四、函数对比
| 函数 | 用途 | 保留字符 | 适用场景 |
|---|---|---|---|
encodeURI | 编码完整 URI | ;/?:@&=+$,# | 编码整个 URL |
encodeURIComponent | 编码 URI 组件 | 无 | 编码 URL 参数、路径片段 |
decodeURI | 解码完整 URI | ;/?:@&=+$,# | 解码由 encodeURI 编码的字符串 |
decodeURIComponent | 解码 URI 组件 | 无 | 解码任意 percent 编码 |
五、编码对照表
不编码的字符(alwaysUnescaped)
A-Z a-z 0-9 - . ! ~ * ' ( )encodeURI 额外不编码
; / ? : @ & = + $ , #特殊字符编码示例
| 字符 | encodeURI | encodeURIComponent |
|---|---|---|
| 空格 | %20 | %20 |
| 中文 | %E5%BC%A0 | %E5%BC%A0 |
= | = | %3D |
# | # | %23 |
/ | / | %2F |
六、常见错误
6.1 URIError
- 无效的 percent 编码:如
%GG - 不完整的转义序列:如
%20后没有足够字符 - 无效的 UTF-8 序列:如过短的续字节
- 未配对的代理码:在编码时遇到孤立的 surrogate
6.2 常见误用
// 错误:URL 查询参数应该用 encodeURIComponent
const url = "https://example.com?param=" + encodeURI("张三");
// 正确
const url = "https://example.com?param=" + encodeURIComponent("张三");七、总结
编码选择:
- 编码整个 URL →
encodeURI - 编码 URL 参数值 →
encodeURIComponent
- 编码整个 URL →
解码选择:
- 解码完整 URL →
decodeURI - 解码任意编码字符串 →
decodeURIComponent
- 解码完整 URL →
底层实现:
Encode和Decode是核心算法ParseHexOctet用于解析十六进制字节
UTF-8 编码:JavaScript 内部使用 UTF-16 字符串,编码时需转换为 UTF-8 字节序列
参考
- ECMAScript 2025 Language Specification
- RFC 2396 (URI Generic Syntax)
- RFC 3629 (UTF-8)