请教了 GPT 老师,大概能理解前端里奇奇怪怪的 length、字符、utf-8、utf-16 的问题了,下面贴一下 GPT 老师的汇总。
一、JS 字符串长度和字符的本质
1. JS 中 string.length 计算的是什么?
- JS 字符串底层用 UTF-16 编码,每个元素是 16-bit 单元(code unit)
string.length返回的是字符串中UTF-16 code unit 的数量- 这导致像 emoji 这种用两个 code unit 表示的字符,
length会大于 1
2. 为什么 JS 用 UTF-16 而不是 UTF-8?
- 设计时基于 UTF-16,方便按固定单元访问字符
- UTF-16 是定长或双单元定长编码,适合字符串索引操作
- UTF-8 变长且复杂,不利于快速随机访问
3. “字符”到底指什么?
- 视觉上的“一个字符”是用户感知的“码点”或“grapheme cluster”
- JS 中
length是 code unit 数,不一定对应一个“字符”或“视觉符号” - 处理 emoji、复合字符时,
length和视觉字符数不匹配
二、JS 字符串与 Unicode 转换示例
- Unicode 码点如 U+1F1E8 是一个完整字符 🇨🇳
- 在 JS 中需转为 UTF-16 surrogate pair 才能正确表示和显示
- 例如使用
String.fromCodePoint(0x1F1E8)创建完整字符 🇨
三、前端“中文算2,英文算1”的长度计算由来
1. 为什么以前前端要这样算?
- 早期后端多用 GBK 等编码,中文占 2 字节,英文占 1 字节
- 数据库存储长度限制按字节数计算,非字符数
- 前端为防止超长导致后端报错,用“中文算2,英文算1”的经验法则估算字节长度
2. 相关代码示例
str.replace(/[^\x00-\xff]/g, 'aa').length;
- 正则匹配非单字节字符(即中文),替换成两个字符
'aa',让中文“长度”算2 \x00-\xff表示 0 到 255 的字节范围,也就是单字节字符(ASCII 及扩展 Latin-1),因此,中文、emoji、其他非单字节字符都会被匹配到
四、MySQL 中 VARCHAR(10) 是什么含义?
VARCHAR(10)表示最多存储 10 个字符(Unicode 码点)- 不是字节数,也不是视觉宽度
- 实际占用字节数根据字符编码(utf8mb4)不同,英文1字节,中文3字节,emoji 4字节
五、MySQL 变长字符串为什么要加长度前缀?
- 数据库行数据是连续存储的
- 变长字段(
VARCHAR)需要前缀标明字节长度,方便定位字段边界 - 这个长度前缀一般是1或2字节,根据最大长度决定
- 避免扫描或依赖终止符,提高读写效率
六、编码细节对字符串长度的影响
| 编码类型 | 中文占用字节 | 备注 |
|---|---|---|
| GBK | 2 字节 | 早期中文编码,双字节固定长度 |
| UTF-8 | 3 字节 | Unicode 编码,变长编码 |
| UTF-16 | 2 或 4 字节 | JS 内部编码,surrogate pair |
七、总结
- JS 字符串长度是 UTF-16 code unit 计数,不是字符个数
- 早期“中2英1”是为配合后端 GBK 编码的字节限制而生
- MySQL
VARCHAR(n)限制的是字符数,实际字节数根据编码变化 - 变长字段需长度前缀保证数据库能正确读写数据
- 理解编码和存储,有助于前后端协作避免长度问题