ES6有关Unicode的连带扩充

面前的话

  JS中的字符串类型是由引号括起来的一组由16位Unicode字符组成的字符体系。在过去,16位可以包含其他字符,直到Unicode引入了扩展字符集,编码规则不得不举行转移。本文将详细介绍ES6有关Unicode的有关扩大

 

概述

  Unicode的目的是为世界上每一个字符提供唯一标识符,唯一标识符称为码位或码点(code
point)。而那一个码位是用来表示字符的,又称为字符编码(character encode) 

  在ES6往日, JS 的字符串以 16 位字符编码(UTF-16)为根基。每个 16
位连串(相当于2个字节)是一个编码单元(code
unit),可简称为码元,用于表示一个字符。字符串所有的习性与办法(如length属性与charAt()方法等)都是依照16位系列

【BMP】

  最常用的Unicode字符使用16位体系编码字符,属于“基本多语种平面”(Basic
Multilingual Plane BMP),也称为“零断面”(plan 0),
是Unicode中的一个编码区段,编码介于U+0000——U+FFFF之间。超越这多少个范围的码位则要归属于某个扶助平面或称为扩张平面(supplementary
plane),其中的码位仅用16位就不可以代表了

  为此,UTF-16引入了代办对(surrogate
pairs),规定用两个16位编码来代表一个码位。这意味着,字符串里的字符有二种:一种由一个码元(共
16 位)来代表BMP字符,另一种用五个码元(共 32 位)来表示声援平面字符

 

大括号表示

  JavaScript 允许利用\uxxxx花样表示一个字符,其中xxxx意味着字符的
Unicode 码位

// "a"
console.log("\u0061");

  但是,这种表示法只限于码位在\u0000~\uFFFF期间的字符。超出这一个限制的字符,必须用多少个双字节的花样表示

// "𠮷"
console.log("\uD842\uDFB7");

// "₻7"
console.log("\u20BB7");

  上边代码表示,要是直接在\u末尾跟上跨越0xFFFF的数值(比如\u20BB7),JavaScript会明白成\u20BB+7。所以会来得一个特殊字符,前面跟着一个7

  ES6 对这点做出了立异,只要将码位放入大括号,就能科学解读该字符

// "𠮷"
console.log("\u{20BB7}");

// "ABC"
console.log("\u{41}\u{42}\u{43}");

let hello = 123;
// 123
console.log(hell\u{6F}); 

// true
console.log('\u{1F680}' === '\uD83D\uDE80');

  上边代码中,最终一个例子声明,大括号表示法与四字节的 UTF-16
编码是等价的。

  有了这种表示法之后,JavaScript 共有6种形式可以代表一个字符

'\z' === 'z'  // true
'\172' === 'z' // true
'\x7A' === 'z' // true
'\u007A' === 'z' // true
'\u{7A}' === 'z' // true

 

字符编解码

【codePointAt()】

  ES6新增了一心匡助UTF-16的艺术codePointAt(),该方法接受编码单元的地方而非字符地点作为参数,重临与字符串中加以地方对应的码位,即一个整数值

var text = "𠮷a" ;

console.log(text.charCodeAt(0)); // 55362
console.log(text.charCodeAt(1)); // 57271
console.log(text.charCodeAt(2)); // 97

console.log(text.codePointAt(0)); // 134071
console.log(text.codePointAt(1)); // 57271
console.log(text.codePointAt(2)); // 97

  对于BMP字符,codePointAt()方法的重临值与 charCodeAt()相同,如’a’,都回来97

  对于匡助平面的32位字符,如’ஷ’,charCodeAt()和codePointAt()方法都分为两有些再次来到

  charCodeAt(0)和chatCodeAt(1)分别重临前16位和后16位的编码;而codePointAt(0)和codePointAt(1)分别再次回到32位编码及后16位的编码 

  判断一个字符是否是BMP,对该字符调用 codePointAt()方法就是最简便易行的主意

function is32Bit(c) {
    return c.codePointAt(0) > 0xFFFF;
}
console.log(is32Bit("𠮷" )); // true 
console.log(is32Bit("a")); // false

  16位字符的上方界用十六进制表示就是FFFF
,由此任何大于该数字的码位必须用五个码元(共32位)表示

【String.fromCodePoint()】

  ES5提供的String.fromCharCode方法,用于从码位重临对应字符,不过这个措施不可以鉴别32位的UTF-16字符

  ECMAScript经常会提供正反二种办法。可以应用codePointAt()来提取字符串内中某个字符的码位,也能够借助String.fromCodePoint()依照给定的码位来生成一个字符

console.log(String.fromCharCode(0x20bb7)); // "ஷ"
console.log(String.fromCodePoint(0x20bb7)); // "𠮷"
console.log(String.fromCharCode(0x0bb7)); // "ஷ"

  下面代码中,String.fromCharCode不可以分辨大于0xFFFF的码位,所以0x20BB7就发出了溢出,最高位2被遗弃了,最终回到码位U+0BB7相应的字符,而不是码位U+20BB7对应的字符

  如果String.fromCodePoint()情势有五个参数,则它们会被合并成一个字符串重临

// true
String.fromCodePoint(0x78, 0x1f680, 0x79) === 'x\uD83D\uDE80y'

  可以将 String.fromCodePoint() 视为 String.fromCharCode()的一揽子版本。两者处理 BMP 字符时会回去相同结果,只有处理 BMP
范围之外的字符时才会有出入

 

for…of

  对于32位的帮手平面字符来说,使用for或for
in循环,可能得不到科学的结果

var s = '𠮷a';
for (let ch in s) {
  console.log(s[ch]);
}
//�
//�
//a

  而for…of循环可以正确的辨别32位的UTF-16字符

var s = '𠮷a';
for (let ch of s) {
  console.log(ch);
}
//𠮷
//a

 

normalize()

  许多非洲语言有语调符号和重音符号。为了表示它们,Unicode提供了二种办法。一种是直接提供带重音符号的字符,比如Ǒ(\u01D1)。另一种是提供合成符号(combining
character),即原字符与重音符号的合成,六个字符合成一个字符,比如O(\u004F)和ˇ(\u030C)合成Ǒ(\u004F\u030C)

  这两种象征方法,在视觉和语义上都等于,不过JavaScript无法鉴别

console.log('\u01D1'==='\u004F\u030C'); //false

console.log('\u01D1'.length); // 1
console.log('\u004F\u030C'.length); // 2

  下边代码表示,JavaScript将合成字符视为两个字符,导致二种象征方法不等于。

  ES6提供字符串实例的normalize()方法,用来将字符的不同代表方法统一为同样的格局,这称之为Unicode正规化

console.log('\u01D1'==='\u01D1'.normalize()); //true
console.log('\u01D1'=== '\u004F\u030C'.normalize()); //true

normalize措施可以接受一个参数来指定normalize的艺术,参数的多少个可选值如下

  1、NFC,默认参数,表示“标准等于合成”(Normalization Form Canonical
Composition),重回三个简易字符的合成字符。所谓“标准等于”指的是视觉和语义上的约等于

console.log('\u01D1'==='\u01D1'.normalize("NFC")); //true
console.log('\u01D1'=== '\u004F\u030C'.normalize("NFC")); //true

  2、NFD,表示“标准等价分解”(Normalization Form Canonical
Decomposition),即在正规非常的前提下,重回合成字符分解的五个简单字符

console.log('\u004F\u030C'==='\u01D1'.normalize("NFD")); //true
console.log('\u004F\u030C'=== '\u004F\u030C'.normalize("NFD")); //true

  3、NFKC,表示“兼容等价合成”(诺玛(Norma)lization Form Compatibility
Composition),再次来到合成字符。所谓“兼容等价”指的是语义上存在相当,但视觉上不等价,比如“囍”和“喜喜”。(这只是用来比喻,normalize方法无法分辨中文。)

  4、NFKD,表示“兼容等价分解”(Normalization Form Compatibility
Decomposition),即在配合等价的前提下,再次来到合成字符分解的多个大概字符

  在支付国际化应用时,normalize()方法异常有效。但normalize()主意近年来不可能辨别五个或四个以上字符的合成。这种情况下,依然只可以动用正则表明式,通过Unicode编号区间判断

 

U修饰符

  正则表明式可以完成简单的字符串操作,但默认将字符串中的每一个字符遵照16位编码处理。为领悟决那几个题目, ES6
对正则表达式添加了u修饰符,含义为“Unicode格局”,用来正确处理大于\uFFFF
Unicode 字符。也就是说,会正确处理多少个字节的 UTF-16 编码

/^\uD83D/u.test('\uD83D\uDC2A') // false
/^\uD83D/.test('\uD83D\uDC2A') // true

  一旦为正则表明式设置了 u
修饰符,正则表明式将会识别32位的接济平面字符为1个字符,而不是六个

【点号】

  点(.)字符在正则表明式中,含义是除了换行符以外的任意单个字符。对于码位大于0xFFFF
Unicode 字符,点字符不可能鉴别,必须抬高u修饰符

var text = "𠮷" ;
console.log(text.length); // 2
console.log(/^.$/.test(text));//false
console.log(/^.$/u.test(text)); //true

【大括号】

  ES6 新增了使用大括号表示 Unicode
字符,这种表示法在正则表明式中务必抬高u修饰符,才能识别当中的大括号,否则会被解读为量词

/\u{61}/.test('a') // false
/\u{61}/u.test('a') // true
/\u{20BB7}/u.test('𠮷') // true

【量词】

  使用u修饰符后,所有量词都会不错识别码点大于0xFFFF的 Unicode 字符

/a{2}/.test('aa') // true
/a{2}/u.test('aa') // true
/𠮷{2}/.test('𠮷𠮷') // false
/𠮷{2}/u.test('𠮷𠮷') // true

【预定义情势】

  u修饰符也影响到预定义格局,能否正确识别码点大于0xFFFF的 Unicode
字符

/^\S$/.test('𠮷') // false
/^\S$/u.test('𠮷') // true

【字符串长度】

  上边代码的\S是预定义形式,匹配所有不是空格的字符。只有加了u修饰符,它才能正确匹配码点超越0xFFFF
Unicode 字符

  尽管ES6不辅助字符串码位数量的检测,length属性依旧重临字符串编码单元的数据。利用[\s\S],再加上u修饰符,就可以写出一个不利再次来到字符串长度的函数

function codePointLength(text) {
  var result = text.match(/[\s\S]/gu);
  return result ? result.length : 0;
}

var s = '𠮷𠮷';

console.log(s.length); // 4
console.log(codePointLength(s)); // 2

【检测援助】

  u修饰符是语法层面的更改,尝试在不般配 ES6 的 JS
引擎中应用它会抛出语法错误。假如要检测当前发动机是否援助u修饰符,最安全的主意是经过以下函数来判定

function hasRegExpU() {
    try {
        var pattern = new RegExp(".", "u");
        return true;
    } catch (ex) {
        return false;
    }
}

  那些函数使用了RegExp构造函数并传到字符串’u’作为参数,该语法固然在旧版
JS 引擎中也是卓有效用的。不过,倘若当前引擎不辅助u修饰符则会抛出错误