跳到主要内容

JavaScript 字符串编码

在JavaScript编程中,字符串编码是一个基础但非常重要的概念。正确理解和处理字符串编码可以帮助你避免国际化文本处理中的许多常见问题,同时确保你的应用能够正确地显示和处理各种语言和特殊字符。

字符编码基础

字符编码是将字符转换为计算机可以存储和传输的数字形式的系统。在JavaScript中,字符串默认使用UTF-16编码。

什么是UTF-16?

UTF-16是一种可变长度的字符编码,用于表示Unicode字符集中的文本。它使用1个或2个16位码元来编码字符。

JavaScript 中的Unicode支持

JavaScript字符串是基于Unicode字符集的,这意味着它可以表示几乎所有的语言和符号。

javascript
// 创建包含各种语言字符的字符串
const multilingual = "Hello, 你好, こんにちは, مرحبا, Привет";
console.log(multilingual);
// 输出: Hello, 你好, こんにちは, مرحبا, Привет

Unicode码点

每个Unicode字符都有一个唯一的数字标识符,称为码点(code point)。在JavaScript中,我们可以使用\u后跟四位十六进制数来表示Unicode字符。

javascript
// 使用Unicode转义序列表示字符
const heart = "\u2764"; // ❤ 的Unicode码点
console.log(heart); // 输出: ❤

对于超出基本多语言平面(BMP)的字符(码点大于U+FFFF的字符),需要使用两个Unicode转义序列或使用ES6引入的扩展表示法:

javascript
// 表示emoji字符 - 笑脸 (U+1F600)
// 旧方法 - 使用代理对
const oldWayEmoji = "\uD83D\uDE00";
// ES6方法 - 使用花括号扩展
const newWayEmoji = "\u{1F600}";

console.log(oldWayEmoji); // 输出: 😀
console.log(newWayEmoji); // 输出: 😀

字符串编码相关方法

JavaScript提供了几种处理字符串编码的方法:

charCodeAt() 和 codePointAt()

charCodeAt()方法返回指定位置字符的UTF-16编码(16位无符号整数):

javascript
const str = "Hello";
console.log(str.charCodeAt(0)); // 输出: 72 (字符'H'的UTF-16码值)

charCodeAt()无法正确处理大于U+FFFF的Unicode字符。ES6引入的codePointAt()方法解决了这个问题:

javascript
const emoji = "😀";
console.log(emoji.charCodeAt(0)); // 输出: 55357 (第一个代理对的值)
console.log(emoji.codePointAt(0)); // 输出: 128512 (完整的码点值)

String.fromCharCode() 和 String.fromCodePoint()

这两个方法是上述方法的逆操作,用于从码值创建字符:

javascript
// 从UTF-16码值创建字符
console.log(String.fromCharCode(72, 101, 108, 108, 111)); // 输出: Hello

// 从码点创建高代码点字符
console.log(String.fromCodePoint(128512)); // 输出: 😀

字符串编码转换

有时需要在不同编码之间转换字符串,尤其是在处理网络请求或文件操作时。

encodeURI() 和 decodeURI()

这对函数用于编码和解码完整的URI:

javascript
const url = "https://example.com/search?q=JavaScript 编程";
const encodedUrl = encodeURI(url);
console.log(encodedUrl);
// 输出: https://example.com/search?q=JavaScript%20%E7%BC%96%E7%A8%8B

console.log(decodeURI(encodedUrl));
// 输出: https://example.com/search?q=JavaScript 编程

encodeURIComponent() 和 decodeURIComponent()

这对函数用于编码和解码URI的单个组件,它们会编码更多的字符:

javascript
const searchTerm = "JavaScript & 编程";
const encoded = encodeURIComponent(searchTerm);
console.log(encoded);
// 输出: JavaScript%20%26%20%E7%BC%96%E7%A8%8B

console.log(decodeURIComponent(encoded));
// 输出: JavaScript & 编程

字符串长度和索引

处理多字节字符时,字符串的长度和索引可能会出现意外行为:

javascript
const emoji = "😀";
console.log(emoji.length); // 输出: 2 (而不是1,因为它由两个UTF-16码元组成)

const text = "café";
console.log(text.length); // 输出: 4

正确计算字符数量

要正确计算Unicode字符的数量,可以使用扩展运算符或Array.from():

javascript
const text = "café😀";
console.log(text.length); // 输出: 6 (5个视觉字符+1个额外码元)

// 正确计算字符数量
console.log([...text].length); // 输出: 5
console.log(Array.from(text).length); // 输出: 5

实际应用场景

场景1:表单输入验证

在处理用户名输入时,你可能想限制字符数而非字节数:

javascript
function validateUsername(username) {
const realLength = [...username].length;

if (realLength < 3 || realLength > 20) {
return "用户名必须在3到20个字符之间";
}
return "用户名有效";
}

console.log(validateUsername("王")); // 输出: 用户名必须在3到20个字符之间
console.log(validateUsername("王小明")); // 输出: 用户名有效
console.log(validateUsername("user_with_emoji😀")); // 输出: 用户名有效

场景2:按字符截取字符串

如果需要按真实字符数(而非码元数)截取字符串:

javascript
function truncateString(str, maxLength) {
if ([...str].length <= maxLength) {
return str;
}

return [...str].slice(0, maxLength).join('') + '...';
}

const longText = "这是一段包含emoji😀的长文本,需要被截断";
console.log(truncateString(longText, 10));
// 输出: 这是一段包含emoji...

场景3:国际化URL处理

当处理包含国际字符的URL时:

javascript
function createSafeUrl(baseUrl, params) {
const url = new URL(baseUrl);

for (const [key, value] of Object.entries(params)) {
url.searchParams.append(key, value);
}

return url.toString();
}

const baseUrl = "https://example.com/search";
const params = {
q: "JavaScript 编程技巧",
lang: "中文"
};

console.log(createSafeUrl(baseUrl, params));
// 输出类似: https://example.com/search?q=JavaScript+%E7%BC%96%E7%A8%8B%E6%8A%80%E5%B7%A7&lang=%E4%B8%AD%E6%96%87

总结

JavaScript的字符串编码系统基于UTF-16,这使得它能够处理几乎所有人类语言的文本。要正确处理字符串编码,需要记住:

  • JavaScript中的字符串长度基于UTF-16码元计数,而非视觉字符
  • 使用codePointAt()String.fromCodePoint()处理完整的Unicode字符
  • 使用扩展运算符或Array.from()获取真实字符数量
  • 使用encodeURI/encodeURIComponent处理URL中的非ASCII字符

掌握这些知识对于开发国际化应用程序尤为重要,它能帮助你避免与字符编码相关的常见问题。

练习

为了巩固你的学习,试着完成以下练习:

  1. 编写一个函数,统计一个字符串中包含的实际Unicode字符数
  2. 创建一个函数,反转一个包含表情符号和其他Unicode字符的字符串
  3. 实现一个函数,检测一个字符串是否只包含特定语言的字符(如只有中文字符)
扩展阅读

要深入了解JavaScript字符串编码,可以参考: