[关闭]
@xiaoyixy 2018-11-08T09:05:36.000000Z 字数 5617 阅读 979

ES6 字符串的扩展

Note ES6 ECMAScript6


unicode, codepoint, UTF

unicode:计算机科学领域里的一项业界标准,包括字符集、编码方案等。计算机采用八比特一个字节,一个字节最大整数是255,还要表示中文一个字也是不够的,至少需要两个字节,为了统一所有的文字编码,unicode为每种语言中的每个字符设定了统一并且唯一的二进制编码,通常用两个字节表示一个字符,所以unicode每个平面可以组合出65535种不同的字符,一共17个平面。

unicode 在计算机中用 unicode 字符集转换格式,即我们常见的 UTF-8、UTF-16 等。

UTF编码:全称是Unicode Transformer Format,这种编码是UCS(Universal Mutiple-Octet Doded Character Set,国际标准ISO10646规定的通用字符集)的实际形式,它的分类是按照其基本长度所占用的位数而定,分为UTF-8/16/32三种形式。UTF可以说是其他字符集的集合,它使得其它字符集是交叉兼容的,可以说,凡是将文字符号转为UCS后再转回原来的编码,也不会丢失信息。UCS包含了现在所有的已知语言的字符,包含从拉丁文、希腊语到中文、韩文等象形文字,再到日文的平假名、片假名等众多语系。因此使用UTF进行程序开发,绝对是程序国际化的首选,Unicode将世界的语言统一起来,构成了最伟大的字符集。

UTF-8 就是以字节为单位对 unicode 进行编码,对不同范围的字符使用不同长度的编码。

Unicode Utf-8
000000-00007F 0xxxxxxx
000080-0007FF 110xxxxx 10xxxxxx
000800-00FFFF 1110xxxx 10xxxxxx 10xxxxxx
010000-10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
  1. // 汉字“编” \u7f16 的 utf-8 编码
  2. 1. 7f16 0800-FFFF 之间,所以要用3字节模板:1110xxxx 10xxxxxx 10xxxxxx
  3. 2. 7f16 写成二进制是:0111 1111 0001 0110
  4. 3. 按三字节模板分段方法分为0111 111100 010110,代替模板中的x,得到11100111 10111100 10010110
  5. 4. “编” 对应的utf-8的编码是e7 bc 96,占3个字节

JavaScript 内部,字符以 UTF-16 的格式储存,每个字符固定为 2 个字节。对于那些需要 4 个字节储存的字符(Unicode 码点大于0xFFFF的字符),JavaScript 会认为它们是两个字符。

codepoint: 与一个Unicode编码表中的某个字符对应的代码值。

JavaScript 的字符

JavaScript 允许采用\uxxxx形式表示一个字符,其中xxxx表示字符的 Unicode 码点。

  1. "\u0061" // "a"

但是,这种表示法只限于码点在\u0000~\uFFFF之间的字符。超出这个范围的字符,必须用两个双字节的形式表示。如果直接在\u后面跟上超过0xFFFF的数值(比如\u20BB7),JavaScript 会理解成\u20BB+7。

  1. "\uD842\uDFB7" // "𠮷"
  2. "\u20BB7" // "₻7"

ES6 对这一点做出了改进,只要将码点放入大括号,就能正确解读该字符。

  1. "\u{20BB7}" // "𠮷"
  2. "\u{41}\u{42}\u{43}" // "ABC"
  3. let hello = 123;
  4. hell\u{6F} // 123
  5. '\u{1F680}' === '\uD83D\uDE80' // true

JavaScript 共有 6 种方法可以表示一个字符。

  1. '\z' === 'z' // true
  2. '\172' === 'z' // true
  3. '\x7A' === 'z' // true
  4. '\u007A' === 'z' // true
  5. '\u{7A}' === 'z' // true

部分新增方法

str.codePointAt(pos)

方法返回一个 Unicode 编码点值的非负整数。于 ECMAScript 2015 (6th Edition, ECMA-262) 标准化。

  1. 'ABC'.codePointAt(1); // 66
  2. '\uD800\uDC00'.codePointAt(0); // 65536
  3. 'XYZ'.codePointAt(42); // undefined

codePointAt方法返回的是码点的十进制值,如果想要十六进制的值,可以使用toString方法转换一下。

  1. let s = '𠮷a';
  2. s.codePointAt(0).toString(16) // "20bb7"
  3. s.codePointAt(2).toString(16) // "61"
  4. // for...of循环会正确识别 32 位的 UTF-16 字符。
  5. for (let ch of s) {
  6. console.log(ch.codePointAt(0).toString(16));
  7. }
  8. // 20bb7
  9. // 61

codePointAt方法是测试一个字符由两个字节还是由四个字节组成的最简单方法。

  1. function is32Bit(c) {
  2. return c.codePointAt(0) > 0xFFFF;
  3. }
  4. is32Bit("𠮷") // true
  5. is32Bit("a") // false

String.fromCodePoint()

该静态方法返回使用指定的代码点序列创建的字符串。在作用上,正好与codePointAt方法相反。如果传入无效的 Unicode 编码,将会抛出一个RangeError (例如: "RangeError: NaN is not a valid code point")。

  1. String.fromCodePoint(42); // "*"
  2. String.fromCodePoint(65, 90); // "AZ"
  3. String.fromCodePoint(0x404); // "\u0404"
  4. String.fromCodePoint(0x2F804); // "\uD87E\uDC04"
  5. String.fromCodePoint(194564); // "\uD87E\uDC04"
  6. String.fromCodePoint(0x1D306, 0x61, 0x1D307) // "\uD834\uDF06a\uD834\uDF07"
  7. String.fromCodePoint('_'); // RangeError
  8. String.fromCodePoint(Infinity); // RangeError
  9. String.fromCodePoint(-1); // RangeError
  10. String.fromCodePoint(3.14); // RangeError
  11. String.fromCodePoint(3e-2); // RangeError
  12. String.fromCodePoint(NaN); // RangeError

ES5 提供 String.fromCharCode() 方法,用于从码点返回对应字符,但是这个方法不能单独获取在高代码点位上的字符,不能识别 32 位的 UTF-16 字符(Unicode 编号大于 0xFFFF)。

  1. String.fromCharCode(0x20BB7) // "ஷ"
  2. String.fromCharCode(0x0BB7) // "ஷ"
  3. String.fromCodePoint(0x20BB7) // "𠮷"

for...of

for...of 语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代锚点,并为每个不同属性的值执行语句

ES6 为字符串添加了遍历器接口,使得字符串可以被 for...of 循环遍历。该接口可以识别大于 0xFFFF 的码点,传统的 for 循环无法识别这样的码点。

  1. for (let codePoint of 'foo') {
  2. console.log(codePoint)
  3. }
  4. // "f"
  5. // "o"
  6. // "o"
  7. let text = String.fromCodePoint(0x20BB7);
  8. for (let i = 0; i < text.length; i++) {
  9. console.log(text[i]);
  10. }
  11. // " "
  12. // " "
  13. for (let i of text) {
  14. console.log(i);
  15. }
  16. // "𠮷"

String.prototype.normalize()

str.normalize([form]) 按照指定的一种 Unicode 正规形式将当前字符串正规化

  1. '\u01D1' // "Ǒ"
  2. '\u004F\u030C' // "Ǒ"
  3. '\u01D1'.length // 1
  4. '\u004F\u030C'.length // 2
  5. '\u01D1'==='\u004F\u030C' //false
  6. '\u01D1'.normalize() === '\u004F\u030C'.normalize() // true

模板字符串

模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

  1. `string text`
  2. // 多行字符串
  3. `string text line 1
  4. string text line 2`
  5. // 插入表达式
  6. `string text ${expression} string text`
  7. // 带标签的模板字符串
  8. // 标签函数并不一定需要返回一个字符串
  9. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Nesting_templates
  10. tag `string text ${expression} string text`
  11. // 嵌套模板
  12. const classes = `header ${ isLargeScreen() ? '' :
  13. `icon-${item.isCollapsed ? 'expander' : 'collapser'}` }`;
  1. let message =
  2. SaferHTML`<p>${sender} has sent you a message.</p>`;
  3. function SaferHTML(templateData) {
  4. let s = templateData[0];
  5. for (let i = 1; i < arguments.length; i++) {
  6. let arg = String(arguments[i]);
  7. // Escape special characters in the substitution.
  8. s += arg.replace(/&/g, "&amp;")
  9. .replace(/</g, "&lt;")
  10. .replace(/>/g, "&gt;");
  11. // Don't escape special characters in the template.
  12. s += templateData[i];
  13. }
  14. return s;
  15. }
  1. function tag(strings) {
  2. console.log(strings.raw[0]);
  3. }
  4. tag`string text line 1 \n string text line 2`;
  5. // logs "string text line 1 \n string text line 2" ,
  6. // including the two characters '\' and 'n'
  1. var str = String.raw`Hi\n${2+3}!`;
  2. // "Hi\n5!"
  3. str.length;
  4. // 6
  5. str.split('').join(',');
  6. // "H,i,\,n,5,!"

这表示类似下面这种带标签的模版是有问题的,因为对于每一个ECMAScript语法,解析器都会去查找有效的转义序列,但是只能得到这是一个形式错误的语法:

  1. latex`\unicode`
  2. // 在较老的ECMAScript版本中报错(ES2016及更早)
  3. // SyntaxError: malformed Unicode character escape sequence

ES2018关于非法转义序列的修订

ES2018 放松了对标签模板里面的字符串转义的限制。如果遇到不合法的字符串转义,就返回undefined,而不是报错。值得注意的是,这一转义序列限制只对带标签的模板字面量移除,而不包括不带标签的模板字面量:

  1. let bad = `bad escape sequence: \unicode`;
  2. // SyntaxError: Invalid Unicode escape sequence

参考

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注