二、字符串与正则表达式


一、字符串与正则表达式

字符串是编程中最重要的数据类型之一,ES6加强了字符串与正则表达式。

1.1、更好的Unicode支持

在ES6之前,JS的字符串以16位字符编码(UCS-2)为基础。每个16位序列都是一个码元(code unit),用于表过一个字符。字符串所有的属性与方法(length与charAt())都是基于16位码元。当然,16位曾经足以容纳任何字符,然后由于Unicode引入了扩展字符集,这就不再够用了。

UTF-16是变长的字符编码方式,有16位与32位两种情况。JS原先使用的则是固定16位(双字节)的字符编码方式,即UCS-2
16位序列(两个字节)一个码元表示一个字符

1.2 UTF-16代码点

Unicode的明确目标是给世界上所有的字符提供全局唯一标识符,而16位的字符长度限制已不能满足这种需求。这些全球唯一标识符被称为代码点(code points),是从0开始的简单数字。代码点是如你想象的字符代码那样,用一个数字来代表一个字符。字符编码要求将代码点转换为内部一致的码元,而对UTF-16来说,代码点可以由多个码元组成。

在UTF-16中的第一个2的16次方代码点示单个16位码元,这个范围被称为多语言基本平面(BMP)。任何超出该范围的代码点都不能用单个16位码元表示,而是会落在扩展平面。UTF-16引入了代理对来解决这个问题,允许使用两个16位码元来表示单个代码点。这意味着字符串内的任意单个字符都可以用一个码元或两个码元来表示,前者对应基本平面字符,后都对应扩展平面字符。

在ES5中,所有字符串操作都基于16位码元,这表示在处理包含代理对UTF-16字符时会出现预期外的结果,就像这个例子:

const text = ""

console.log(text.length)          // 2
console.log(/^.$/.test(text))     // false
console.log(text.charAt(0))       // ""
console.log(text.charAt(1))       // ""
console.log(text.charCodeAt(0))   // 55362
console.log(text.charCodeAt(1))   // 57271

这个Unicode字符""使用了代理对,因此,上面的JS字符串操作会将该字符串当作两个16位字符串来对待,这意味着:

  • text的长度属性值是2,而不是应有的1
  • 意图匹配单个字符的正则表达式匹配失败了,因为它认为这里有两个字符
  • charAt()方法无法返回一个有效的字符,因为这里每16位代码点都不是一个可打印字符。
  • charCodeAt()方法同样无法正确识别该字符,它只能返回每个码元的16位数字,但在ES5中,这已经是对text变量所能获取到的最精确的值了。

codePointAt()方法:
ES6为全面支持UTF-16而新增的方法之一是codePointAt(),它可以在给定字符串中按位置提取Unicode代码点。该方法接受的是码元位置而非字符位置,并返回一个整数值。

const text = "a"

console.log(text.charCodeAt(0))  // 97
console.log(text.charCodeAt(1))  // NaN
console.log(text.charCodeAt(2))  // NaN

console.log(text.codePointAt(0)) // 97
console.log(text.codePointAt(1)) // undefined
console.log(text.codePointAt(2)) // undefined

判断字符包含了一个还是两个码元,对该字符调用codePointAt()就是最简单的方法。

function is32Bit(c){
   
    return c.codePointAt(0) > 0xFFFF
}

console.log(is32Bit('嘎'))
console.log(is32Bit("a"))
console.log('嘎'.codePointAt(0))
console.log(String.fromCharCode(22030))

String.fromCodePoint() 方法
当 ECMAScript 提供了某种方法时,它一般也会给出方法来处理相反的操作。你可以使用codePointAt() 来提取字符串内中某个字符的代码点,也可以借助 String.fromCodePoint()用给定的代码点来产生包含单个字符的字符串。例如:

console.log(String.fromCharCode(22030)) // 嘎

normalize() 方法
Unicode 另一个有趣之处是,不同的字符在排序或其它一些比较操作中可能会被认为是相同的。有两种方式可以定义这种关联性:第一种是规范相等性( canonical equivalence ),意味着两个代码点序列在所有方面都被认为是可互换的。例如,两个字符的组合可以按规范等同于另一个字符。第二种关联性是兼容性( compatibility ),两个兼容的代码点序列看起来有差别,但在特定条件下可互换使用。

由于这些关联性,文本内容在根本上相同的两个字符串就可以包含不同的代码点序列。例如,字符 “æ” 与双字符的字符串 “ae” 或许能互换使用,但它们并不严格相等,除非使用某种手段来标准化。

ES6 给字符串提供了 normalize() 方法,以支持 Unicode 标准形式。
该方法接受单个可选的
字符串参数,用于指示需要使用下列哪种 Unicode 标准形式:
Normalization Form Canonical Composition ( “NFC” ),这是默认值;
Normalization Form Canonical Decomposition ( “NFD” );
Normalization Form Compatibility Composition ( “NFKC” );
Normalization Form Compatibility Decomposition ( “NFKD” )。
只需记住,当比较字符串时,它们必须被标准化为同一种形式。例如:

此代码将 values 数组中的字符串转换为一种标准形式,以便让转换后的数组可以被正确排序。你也可以在比较过程中调用 normalize() 来对原始数组进行排序。如下所示:

values.sort(function(first, second){
   
    let firstNormalized = first.normalize()
    let secondNormalized = second.normalize()
    if(firstNormalized<secondNormalized){
   
        return -1
    }else if (firstNormalized===secondNormalized){
   
        return 0
    }else{
   
        return 1
    }
})

新方法并不是 ES6 为 Unicode 字符串提供的唯一改进,它还新增了两个有用的语法要素。

正则表达式 u 标志
你可以使用正则表达式来完成字符串的很多通用操作。但要记住,正则表达式假定单个字符使用一个 16 位的码元来表示。为了解决这个问题, ES6 为正则表达式定义了用于处理Unicode 的 u 标志。

当一个正则表达式设置了 u 标志时,它的工作模式将切换到针对字符,而不是针对码元。这意味着正则表达式将不会被字符串中的代理对所混淆,而是会如预期那样工作。例如,研究以下代码:

var text = "a"

console.log(text.length)
console.log(/^.$/.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

永恒的宁静

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值