JavaScript中的字符串究竟是什么?
1.通过可见字符建模
对JavaScript字符串进行心理建模的最简单的方法,但并不完全准确,就是通过字母、数字和标点符号等字符的序列。
下面的代码样本中的字符串由5个字母和一个感叹号组成。
const message = 'Hello!';
把字符串看成是一个可见字符的序列,也表明'Hello!' 字符串中的字符数等于6。
const message = 'Hello!'; message.length; // => 6
如果字符来自基本的拉丁字符块,也就是127个ASCII字符,那么用可见字符(字形)来模拟字符串的方法就很有效。
但是,一旦你处理更复杂的字符,例如表情符号(😀,😁,😈),用可见字符对字符串进行建模就不准确了。
考虑一下下面这个字符串。
const smile = '😀';
你可以看到,这个字符串只包含一个字符:咧嘴笑的脸。
但是如果你使用smile.length 属性来确定字符的数量,你可能会惊讶地发现它包含2个单位。
const smile = '😀';
smile.length; // => 2
怎么会这样:你看到的是一个字符,而length 表示有2个?
这是因为JavaScript认为字符串是一个代码单元的序列,而不是一个可见的字符序列。
让我们更详细地看看JavaScript中的字符串是什么。
2.通过代码单元建模
规范中说到了JavaScript中的字符串是什么。
字符串类型是由0个或多个16位无符号整数值("元素")组成的所有有序序列的集合。在运行的ECMAScript程序中,String类型通常用来表示文本数据,在这种情况下,String中的每个元素都被当作UTF-16代码单元值。
简单地说,JavaScript中的字符串是一串数字,正是UTF-16代码单位值。
一个代码单位只是一个数字,从0x0000 到0xFFFF 。神奇的事情发生了,因为在代码单元值和特定的字符之间存在着一种映射关系。
例如,代码单位0x0048 ,通过unicode转义序列 \u0048 ,被呈现为实际字符H 。
const letter = '\u0048';
letter === 'H' // => true
现在让我们直接使用UTF-16代码单元来创建'Hello!' 字符串。
const message = '\u0048\u0065\u006C\u006C\u006F\u0021';
message === 'Hello!'; // => true
message.length; // => 6
\u0048\u0065\u006C\u006C\u006F\u0021 这就是JavaScript如何看待字符串的:作为一个代码单元的序列。请注意,所呈现的序列有6个代码单元,这与'Hello!' 字符串中的可见字符数相对应。
在UTF-16中,一个来自基本多语言平面的Unicode字符被编码为一个代码单元。
然而,来自非基本多语言平面的字符:
需要一对不可分割的代码单元(名为代理对)来进行UTF-16编码。
例如,笑脸字符'😀' ,它的编码单位是0x1F600 (数字0x1F600 比0xFFFF 大,因此不适合在16位中使用),用2个编码单位的序列0xD83D0xDE00 。
const smile = '\uD83D\uDE00';
smile === '😀'; // => true
smile.length; // => 2
序列\uD83D\uDE00 是一个特殊的对子,名为代理对子。
smile.length 评估为 ,这表示字符串基元的 属性决定了2 length 代码单元的数目。
字符串迭代器知道代用对。当你调用字符串迭代器时,例如使用传播操作符... ,它将一个代理对计算为一个长度单位。
const message = 'Hello!';
const smile = '😀';
[...message].length; // => 6
[...smile].length; // => 1
3.总结
思考JavaScript字符串的最简单的方法是一个可见字符的序列。这种方法对英文字母、数字、ASCII字符都很有效。
然而,严格地说,JavaScript中的字符串是UTF-16代码单元的序列。string.length 属性决定了代码单元的数量,而不是可见字符的数量。