Unicode扩展

前面的话

在ES6之前,js的字符串以16位字符编码为基础,16位即两个字节表示一个字符。采用\uxxxx形式表示一个字符,其中xxxx 表示字符的Unicode码点。本文将介绍ES6 关于Unicode的扩展。

●大括号表示法

ES6之前的写法:

 {
        // a
        console.log('\u0061');
 }

但这种表示方法只限于码点在\u0000 ~ \uFFFF之间的字符。超出这个范围的字符,必须用 2个双字节的形式表示。

   {
        console.log("\uD842\uDFB7");// ?
        console.log('\u20BB7');// ₻7
    }

上面代码表示,如果直接在\u后面跟上超过0xFFFF的数值(比如\u20BB7),js会理解成\u20BB + 7,由于\u20BB是一个不可打印的字符,所以会显示一个特殊字符,后面跟一个7。

ES6的改进:

只要将码点放入大括号,就能正确解读该字符。

  {
        console.log('\u{20BB7}');// ?
        console.log('\u{41}\u{42}\u{43}');// ABC
        let hello = 123;
        console.log('hell\u{6F}');// hello
        console.log('\u{1F680}'=== '\uD83D\uDE80');// true 
    }

上面最后一个这个例子表明:大括号表示法与四字节的UTF-16编码是等价的

有了这种表示法,js共有6种方法表示一个字符:

 {
        console.log('\z' === 'z');
        console.log('\172' === 'z');
        console.log('\x7A' === 'z');
        console.log('\u007A' === 'z');
        console.log('\u{7A}' === 'z');
    }
●codePointAt()

在js内部,每个字符固定为2个字节。对于那些需要用4个字节存储的字符(Unicode码点大于0xFFFF的字符),js会认为他们是2个字符。

es6之前对于这种码点大于0xFFFF的字符,js无法正确处理,误判为字符串长度为2。chatAt()无法读取整个字符,charCodeAt()只能返回前2个字节和后2个字节的值(且为10进制)

{
    var s = '?';
    console.log(s.length);// 2
    console.log(s.charAt(0));// �
    console.log(s.charAt(1));// �
    console.log(s.charCodeAt(0));// 55362
    console.log(s.charCodeAt(1));// 57271
}

ES6提供的codePointAt()方法,能够正确处理4个字节存储的字符,返回一个字符的码点。

  {
        var s = '?a';
        console.log(s.codePointAt(0));// 134071
        console.log(s.codePointAt(1));// 57271
        console.log(s.codePointAt(2));// 97
    }

上面这个例子:js将’?a’视为3个字符,codePointAt()在第一个字符上正确的识别了‘?’,返回了它的十进制码点134071。在第二个字符(即‘?’的后面两个字节)和第三个字符‘a’上,codePointAt()的结果与charCodeAt()相同。

● String.fromCodePoint()

ES6提供的String.fromCodePoint()与codePointAt()正好相反,它是根据码点返回对应的字符.

ES6之前的String.fromCharCode()方法不能识别占4个字节的字符。

{
   console.log( String.fromCharCode(0x20BB7));// ஷ
}

ES6提供的String.fromCodePoint(),可以识别大于0xFFFF的字符,弥补了String.fromCharCode()的不足

{
 console.log(String.fromCodePoint(0x20BB7));// ?
console.log(String.fromCodePoint(0x78,0x1f680,0x79));// x?y
console.log(String.fromCodePoint(0x78,0x1f680,0x79) === 'x\uD83D\uDE80y');// true
}

注意: fromCodePoint方法定义在String对象上,而CodePointAt方法定义在字符串实例上

● for …of

对于32位的UTF-16字符,使用for或则for…in可能得不到正确的结果

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

利用for … of可以得到正确的结果

  {
        let s = String.fromCodePoint(0x20BB7,0x61);
        for(let ch of s){
            console.log(ch);
        }
        // ? 
        // a
  }
● normalize()

许多欧洲语言有语调符号和重音符号,为了表示他们,Unicode提供了两种方法。一种是直接提供带重音符号的字符,比如Ǒ(\u01D1)。另一种是提供合成符号,即原字符与重音符号合成一个字符,比如o(\u004F)和(\u030C)合成Ǒ(\u004F\u030C)

这两种表示方法在视觉上和语义上都是等价的,但是js无法识别,js将合成的字符视为两个字符,导致两种表示方法不等价。

{
    console.log('\u01D1' === '\u004F\u030C');// false
    console.log('\u01D1'.length);// 1
    console.log('\u004F\u030C'.length);// 2
}

ES6 为提供了normalize方法,用来将字符的不同表示方法统一为同样的形式,着称为Unicode正规化

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

normalize()方法可以接受一个参数来指定normalize的方式,参数的4个可选值:

1: NFC ,默认参数,表示“标准等价合成”,返回多个简单的字符合成字符。所“标准等价”指的是视觉和语义上的等价
2:NFD,表示“标准等价分解”,即在标准等价的前提下,返回合成字符分解出的多个简单字符
3:NFKC,表示“兼容等价合成”,返回合成字符,所谓“兼容等价”指的是语义上的等价,视觉上不等价,比如“囍”和“喜喜”。(这里 只是举例,normalize()并不能识别中文)

4:NFKD,表示“兼容等价分解”,即在兼容等价的前提下,返回合成字符分解出的多个简单字符。

  console.log('\u004F\u030C'.normalize('NFC').length);// 1
  console.log('\u004F\u030C'.normalize('NFD').length);// 2

上面这个例子表示,NFC参数返回字符的合成形式,NFD参数返回字符的分解形式。

不过normalize()方法不能识别3个或3个以上的字符的合成。这种情况下,还是只能使用正则表达式,通过Unicode编号区间来判断。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值