字符串的 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!'