Skip to content

最早,由于存储空间非常宝贵,Unicode 使用 16 位二进制来存储文字,16 位二进制一共也就能表示六万多个字符,显然不太够,所以后来由于技术发展,Unicode 对文字编码进行了扩展,将某些文字扩展到了 32 位。我们一般将 16 位二进制叫做一个码元,也就是说以前文字是用一个码元表示的,而后来一些拓展进来的文字就是两个码元来表示。

看到问题了吗,有的文字占用 16 位,有的文字占用 32 位,但实际上,在 JS 的眼中,一个文字就是 16 位,所以在 JS 中,那些占用 32 位的文字实际上是两个文字,当然这也不限于文字,我们可以做这样一个测试:

js
const str = "😻";
console.log(str.length);//2

很显然,这个 😻 也是一个 unicode 字符,但是这个字符就是 32 位的,在 JS 眼里,16 位就是一个字符,所以 JS 就会把它看成是两个字符。

到了 ES 6,这个问题肯定需要想办法解决,但是又不可随意修改 length 属性,否则以前的系统就会出现问题,所以 ES 6 为我们提供了一个属性,叫做 codePointAt() 方法,这个方法很智能,当我们使用 str.codePointAt(0) 方法的时候,我们可以拿到 😻 的 unicode 编码,codePointAt() 他也是把字符看成 16 位的,但是当我们访问 str.codePointAt(0) 的时候,其实也只是只读取 16 位,但是他读取完 16 位发现这个还没完,所以又会往后读 16 位,就能把完整的 32 位读出来。

所以实际上,condePointAt 方法最大的作用就是能判断是否读完了,所以我们可以用这个方法来判断一个字符是 16 位还是 32 位的。

js
function is32bit(char,i){
	return char.codePointAt(i) > 0xffff;
}

有了这个东西,我们就可以得到一个字符串的真实长度

js
function length(str){
	let len = 0;
	for(let i = 0;i<str.length;i++){
		len++;
		if(is32bit(str,i))
			i++;
	}
	return len;
}

在正则表达式中匹配时默认也是把一个字符看做 16 位,在 ES 6 中,如果正则表达式添加了配置 u,则匹配的时候就能解决该问题。

以笔记为镜,可以知得失