字符串的扩展

78 阅读5分钟

字符串的 Unicode 表示法

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

'\u0061' // 'a'

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

'\uD842\uDFB7' // '𠮷'

'\u20BB7' //  '7'

如果直接在 \u 后面跟上超过 0xFFFF 的数值,例如\u20BB7,JavaScript 会理解成 \u20BB+7,由于\u20BB是一个不可打印字符,所以会显示一个空格,后面跟一个 7

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

'\u{20BB7}' // '𠮷'

'\u{41}\u{42}\u{43}' // 'ABC'

codePointAt()

JavaScript 内部,字符以 UTF-16 的格式存储,每个字符固定为 2 个字节,对于那些需要 4 个字节存储的字符,JavaScript 会认为它们是 2 个字符

var s = '𠮷'

s.length // 2
s.chartAt(0) // ''
s.chartAt(1) // ''
s.chartCodeAt(0) // 55362
s.chartCodeAt(1) // 57271

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

var s = '𠮷a'

s.codePointAt(0) // 134071
s.codePointAt(1) // 57271
s.codePointAt(2) // 97

JavaScript 将'𠮷a'视为 3 个字符,codePointAt 方法在第一个字符上正确识别了'𠮷',返回了它的十进制码点134071即十六进制20BB7。在第二个字符和第三个字符上,codePointAt 方法的结果和 chartCodeAt 方法相同

codePointAt 方法的参数仍然是不正确的,字符串 a 在字符串 s 中的正确位置需要应该是 1 。解决这个问题的方法是使用 for…of 循环,它会正确识别 32 位的 UTF-16 字符

var s = '𠮷a'
for(let ch of a) {
    console.log(ch.codePointAt(0).toString(16))
}
// 20bb7
// 61

String.fromCodePoint()

ES5 提供了 String.fromCharCode 方法,用于从码点返回对应的字符,但这个方法不能识别 32 位的 UTF-16 字符(Unicode编号大于 0xFFFF)

String.fromCharCode(0x20BB7) // 'ஷ'

String.fromCharCode 不能识别大于 0xFFFF 的码点,所以 0x20BB7 就会发生溢出,最高位 2 被舍弃,最后返回码点 U+0BB7 对应的字符

ES6 提供了 String.fromCodePoint 方法,可以识别大于 0xFFFF 的字符

ES6 为字符串添加了遍历器接口,使得字符串可以由 for…of 循环遍历

at()

ES5 对字符串对象提供了 charAt 方法,返回字符串给定位置的字符,该方法不能识别码点大于 0xFFFF 的字符

ES6 有一个提案,提出字符串实例的 at 方法,可以识别 Unicode 编号大于 0xFFFF 的字符

normalize()

许多欧洲语言有语调符号和重音符号。Unicode 提供了两种方法

  • 直接提供带重音符号的字符,'Ǒ'(\u01D1)
  • 提供合成符号,将原字符和重音符号合成一个字符,'O'(\u004F)'̌'(\u030C) 合成 'Ǒ'(\u004F\u030C)

但是 JavaScript 无法识别,会认为两种方法不等价

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

includes() 、startsWith()、endsWith()

  • includes():返回布尔值,表示是否找到了参数字符串
  • startsWith():返回布尔值,表示参数字符串是否在源字符串的头部
  • endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部

这三个方法都支持第二个参数,表示开始搜索的位置

使用第二个参数时,endsWith 的行为和其它两个方法有所不同,它针对的是前 n 个字符,而其它两个方法针对的是从第 n 个位置到字符串结束之间的字符

repeat()

repeat 方法返回一个新字符串,表示将原字符串重复 n 次

  • 参数如果是小数,会被取整
  • 参数如果是负数或者 Infinity ,会报错
  • 参数如果是 0 到 -1 之间的小数,等同于 0
  • 参数如果是字符串,会先转化为数字

padStart() 、padEnd()

padStart() 用于头部补全,padEnd() 用于尾部补全

padStart 和 padEnd 接受两个参数

  • 指定字符串的最小长度
  • 用来补全的字符串
'x'.padStart(5, 'ab') // 'ababx'
'x'.padEnd(4, 'ab') // 'xaba'

如果原字符串的长度等于或大于指定的最小长度,则返回原字符串

'xxx'.padStart(2, 'ab') // 'xxx'

如果用来补全的字符串和原字符串的长度之和超过指定的最小长度,则会截去超出位数的补全字符串

'abc'.padStart(10, '0123456789') // '0123456abc'

如果省略第二个参数,则会用空格补全

'x'.padStart(4) // '   x'

常见用途,补全指定位数

'1'.padStart(10, '0') // '0000000001'

提示字符串格式

'12'.padStart(10, 'YYYY-MM-DD') // 'YYYY-MM-12'

模板字符串

模板字符串是增强版的字符串,用反引号(\)表示

在模板字符串中需要使用反引号时,则在其前面用反斜杠转义

在模板字符串中空格和换行都是会保留的,如果不想要换行,可以用 trim 方法消除

在模板字符串中嵌入变量,需要将变量名写在 ${}

  • 大括号内可以放任意 JavaScript 表达式,可以进行运算,以及引用对象属性
  • 大括号内还可以调用函数
  • 如果大括号中的值不是字符串,会将其转化为字符串

标签模板

模板字符串还可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串,这被称为标签模板

alert`123` // 等同于 alert(123)

标签模板其实不是模板,而是函数调用的一种特殊形式

如果模板字符串中有变量,就不是简单的调用了,而是将模板字符串先处理成多个参数,再调用函数

var a = 5
var b = 10

tag`Hello ${a + b} world ${a * b}`

// 等同于

tag(['Heelo ', ' world ', ''], 15, 50)

String.raw()

String.raw 方法用来充当模板字符串的处理函数,返回一个反斜杠都被转移的字符串,对应于替换变量后的模板字符串

String.raw`Hi\n${2+3}!` // 'Hi\\n5!'