Unicode in Javascript

What is unicode

本质上计算机只处理数字,为了处理字符,最初制定了一套 ASCII 编码,对英文字符和数字做了统一规定。一共有 128 个字符编码,例如大写的字母 A 是 65(二进制 01000001)。

但考虑到数百种语言成千上万的不同字符,一个字节八位,最多只能表示 256 种符号,是远远不够的,因此后来发展出许多不同的编码方式来解决这个问题,打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。尤其随着互联网的普及,一个统一的编码成为了一个非常广泛的需求,人们需要有一种编码,将世界上所有的符号都纳入其中,每一个符号都给予一个独一无二的编码,从而解决乱码问题,这就是 Unicode。

Unicode provides a unique number for every character, no matter what the platform, no matter what the program, no matter what the language.

Encoding of source files

在 web 环境中,一个文件的编码方式是如何确定的呢?

如果没有设置过,会按照 local 的字符集。

可以通过 BOM 来设置,不过现在主流的编辑器都不会在文件头添加 BOM 了。

Unicode standard:

… Use of a BOM is neither required nor recommended for UTF-8, but may be encountered in contexts where UTF-8 data is converted from other encoding forms that use a BOM or where the BOM is used as a UTF-8 signature.

W3C says:

In HTML5 browsers are required to recognize the UTF-8 BOM and use it to detect the encoding of the page, and recent versions of major browsers handle the BOM as expected when used for UTF-8 encoded pages. – https://www.w3.org/International/questions/qa-byte-order-mark

如果文件通过 HTTP 协议获取,Content-Type 头可以设置编码方式:

Content-Type: application/javascript; charset=utf-8

如果没有在 HTTP 头部中设置,会检查 script tag 的 charset 属性:

<script src="./app.js" charset="utf-8">

如果也没有设置的话,会检查 document charset meta tag

<head>
  <meta charset="utf-8">
</head>

Inside Javascript

不管 Javascript 文件的编码是什么,Javascript 内部都会在执行之前转换为 UTF-16,as the ECMAScript standard says:

When a String contains actual textual data, each element is considered to be a single UTF-16 code unit.

Unicode in a string

Unicode 可以通过 \uXXXX 的格式来作为字符串使用

const s1 = '\u00E9' // é

也可以通过组合两个 unicode 字符来表示:

const s2 = '\u0065\u0301' // é

注意这两个字符看上去相同,但其实是不同的:

s1 === s2 // false
s1.length // 1
s2.length // 2

也可以通过组合一个普通字符和 unicode 字符:

const s3 = 'e\u0301' // é
s3.length === 2 // true
s2 === s3 // true
s1 !== s3 // true

可以通过 unicode 来检查中文字符,具体可以查看 http://www.unicode.org/reports/tr38/#BlockListing

/[\u4E00-\u9FCC]/ // CJK Unified Ideographs
/[\u3400-\u4DB5]/ // CJK Unified Ideographs Extension A
/[\ud840-\ud868][\udc00-\udfff]|\ud869[\udc00-\uded6]/ // CJK Unified Ideographs Extension B
/\ud869[\udf00-\udfff]|[\ud86a-\ud86c][\udc00-\udfff]|\ud86d[\udc00-\udf34]/ // CJK Unified Ideographs Extension C
/\ud86d[\udf40-\udfff]|\ud86e[\udc00-\udc1d]/ // CJK Unified Ideographs Extension D

当搜索中文信息时你会发现大部分正则都是用的第一个。

Normalize

为了解决上面这种看上去相同实际上是不同组合的问题,ES2015 提供了一个 normalize 的 API

s1.normalize() === s2.normalize() //true

Convert

十进制和十六进制

0x4e00 // decimal: 19968
parseInt('4e00', 16) // 19968, hexdecimal -> decimal
(19968).toString(16) // '4e00', decimal -> hexdecimal

字符和数字转换

String.fromCodePoint(19968)
String.fromCodePoint(0x4e00) // 汉字 `一`

''.codePointAt(0) // 19968

Emojis

Emojis 实际上也是 unicode 字符,因此可以在字符串中使用,具体可以查看 https://unicode.org/emoji/charts/full-emoji-list.html

const s4 = '🐶'

ref