1
0
mirror of https://github.com/ruanyf/es6tutorial.git synced 2025-05-25 03:02:21 +00:00
es6tutorial/docs/string.md
2014-11-23 08:29:46 +08:00

355 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 字符串的扩展
ES6加强了对Unicode的支持并且扩展了字符串对象。
## codePointAt()
JavaScript内部字符以UTF-16的格式储存每个字符固定为2个字节。对于那些需要4个字节储存的字符Unicode编号大于0xFFFF的字符JavaScript会认为它们是两个字符。
```javascript
var s = "𠮷";
s.length // 2
s.charAt(0) // ''
s.charAt(1) // ''
s.charCodeAt(0) // 55362
s.charCodeAt(1) // 57271
```
上面代码中汉字“𠮷”的Unicode编号是0x20BB7UTF-16编码为0xD842 0xDFB7十进制为55362 57271需要4个字节储存。对于这种4个字节的字符JavaScript不能正确处理字符串长度会误判为2而且charAt方法无法读取字符charCodeAt方法只能分别返回前两个字节和后两个字节的值。
ES6提供了codePointAt方法能够正确处理4个字节储存的字符返回一个字符的Unicode编号。
```javascript
var s = "𠮷a";
s.codePointAt(0) // 134071
s.codePointAt(1) // 57271
s.charCodeAt(2) // 97
```
codePointAt方法的参数是字符在字符串中的位置从0开始。上面代码中JavaScript将“𠮷a”视为三个字符codePointAt方法在第一个字符上正确地识别了“𠮷”返回了它的十进制Unicode编号134071即十六进制的20BB7。在第二个字符即“𠮷”的后两个字节和第三个字符“a”上codePointAt方法的结果与charCodeAt方法相同。
总之codePointAt方法会正确返回四字节的UTF-16字符的Unicode编号。对于那些两个字节储存的常规字符它的返回结果与charCodeAt方法相同。
codePointAt方法是测试一个字符由两个字节还是由四个字节组成的最简单方法。
```javascript
String.fromCodePoint(134071) // "𠮷"
```
注意fromCodePoint方法定义在String对象上而codePointAt方法定义在字符串的实例对象上。
## 字符的Unicode表示法
JavaScript允许采用“\uxxxx”形式表示一个字符其中“xxxx”表示字符的Unicode编号。
```javascript
"\u0061"
// "a"
```
但是,这种表示法只限于\u0000——\uFFFF之间的字符。超出这个范围的字符必须用两个双字节的形式表达。
```javascript
"\uD842\uDFB7"
// "𠮷"
"\u20BB7"
// " 7"
```
上面代码表示,如果直接在“\u”后面跟上超过0xFFFF的数值比如\u20BB7JavaScript会理解成“\u20BB+7”。由于\u20BB是一个不可打印字符所以只会显示一个空格后面跟着一个7。
ES6对这一点做出了改进只要将Unicode编号放入大括号就能正确解读该字符。
```javascript
"\u{20BB7}"
// "𠮷"
"\u{41}\u{42}\u{43}"
// "ABC"
```
## 正则表达式的u修饰符
ES6对正则表达式添加了u修饰符用来正确处理大于\uFFFF的Unicode字符。
**1点字符**
点(.)字符在正则表达式中,解释为除了换行以外的任意单个字符。对于大于\uFFFF的Unicode字符点字符不能识别必须加上u修饰符。
```javascript
var s = "𠮷";
/^.$/.test(s) // false
/^.$/u.test(s) // true
```
上面代码表示如果不添加u修饰符正则表达式就会认为字符串为两个字符从而匹配失败。
**2Unicode字符表示法**
ES6新增了使用大括号表示Unicode字符这种表示法在正则表达式中必须加上u修饰符才能识别。
```javascript
/\u{61}/.test('a') // false
/\u{61}/u.test('a') // true
/\u{20BB7}/u.test('𠮷') // true
```
上面代码表示如果不加u修饰符正则表达式无法识别`\u{61}`这种表示法只会认为这匹配属61个连续的u。
**3量词**
使用u修饰符后所有量词都会正确识别大于\uFFFF的Unicode字符。
```javascript
/a{2}/.test('aa') // true
/a{2}/u.test('aa') // true
/𠮷{2}/.test('𠮷𠮷') // false
/𠮷{2}/u.test('𠮷𠮷') // true
```
**4预定义模式**
u修饰符也影响到预定义模式能否正确识别大于\uFFFF的Unicode字符。
```javascript
/^\S$/.test('𠮷') // false
/^\S$/u.test('𠮷')
```
上面代码的`\S`是预定义模式匹配所有不是空格的字符。只有加了u修饰符它才能正确匹配大于\uFFFF的Unicode字符。
利用这一点,可以写出一个正确返回字符串长度的函数。
```javascript
function codePointLength(text) {
var result = text.match(/[\s\S]/gu);
return result ? result.length : 0;
}
var s = "𠮷𠮷";
s.length // 4
codePointLength(s) // 2
```
**5i修饰符**
有些Unicode字符的编码不同但是字型很相近比如\u004B与\u212A都是大写的K。
```javascript
/[a-z]/i.test('\u212A') // false
/[a-z]/iu.test('\u212A') // true
```
上面代码中不加u修饰符就无法识别非规范的K字符。
## normalize()
为了表示语调和重音符号Unicode提供了两种方法。一种是直接提供带重音符号的字符比如Ǒ\u01D1。另一种是提供合成符号combining character即原字符与重音符号的合成两个字符合成一个字符比如O\u004F和ˇ\u030C合成Ǒ\u004F\u030C
这两种表示方法在视觉和语义上都等价但是JavaScript不能识别。
```javascript
'\u01D1'==='\u004F\u030C' //false
'\u01D1'.length // 1
'\u004F\u030C'.length // 2
```
上面代码表示JavaScript将合成字符视为两个字符导致两种表示方法不相等。
ES6提供String.prototype.normalize()方法用来将字符的不同表示方法统一为同样的形式这称为Unicode正规化。
```javascript
'\u01D1'.normalize() === '\u004F\u030C'.normalize()
// true
```
normalize方法可以接受四个参数。
- NFC默认参数表示“标准等价合成”Normalization Form Canonical Composition返回多个简单字符的合成字符。所谓“标准等价”指的是视觉和语义上的等价。
- NFD表示“标准等价分解”Normalization Form Canonical Decomposition即在标准等价的前提下返回合成字符分解的多个简单字符。
- NFKC表示“兼容等价合成”Normalization Form Compatibility Composition返回合成字符。所谓“兼容等价”指的是语义上存在等价但视觉上不等价比如“囍”和“喜喜”。
- NFKD表示“兼容等价分解”Normalization Form Compatibility Decomposition即在兼容等价的前提下返回合成字符分解的多个简单字符。
```javascript
'\u004F\u030C'.normalize(NFC).length // 1
'\u004F\u030C'.normalize(NFD).length // 2
```
上面代码表示NFC参数返回字符的合成形式NFD参数返回字符的分解形式。
不过normalize方法目前不能识别三个或三个以上字符的合成。这种情况下还是只能使用正则表达式通过Unicode编号区间判断。
## contains(), startsWith(), endsWith()
传统上JavaScript只有indexOf方法可以用来确定一个字符串是否包含在另一个字符串中。ES6又提供了三种新方法。
- **contains()**:返回布尔值,表示是否找到了参数字符串。
- **startsWith()**:返回布尔值,表示参数字符串是否在源字符串的头部。
- **endsWith()**:返回布尔值,表示参数字符串是否在源字符串的尾部。
```javascript
var s = "Hello world!";
s.startsWith("Hello") // true
s.endsWith("!") // true
s.contains("o") // true
```
这三个方法都支持第二个参数,表示开始搜索的位置。
```javascript
var s = "Hello world!";
s.startsWith("o", 4) // true
s.endsWith("o", 8) // true
s.contains("o", 8) // false
```
上面代码表示使用第二个参数n时endsWith的行为与其他两个方法有所不同。它针对前n个字符而其他两个方法针对从第n个位置直到字符串结束。
## repeat()
repeat()返回一个新字符串表示将原字符串重复n次。
```javascript
"x".repeat(3) // "xxx"
"hello".repeat(2) // "hellohello"
```
## 正则表达式的y修饰符
除了u修饰符ES6还为正则表达式添加了y修饰符叫做“粘连”sticky修饰符。它的作用与g修饰符类似也是全局匹配后一次匹配都从上一次匹配成功的下一个位置开始不同之处在于g修饰符只确保剩余位置中存在匹配而y修饰符确保匹配必须从剩余的第一个位置开始这也就是“粘连”的涵义。
```javascript
var s = "aaa_aa_a";
var r1 = /a+/g;
var r2 = /a+/y;
r1.exec(s) // ["aaa"]
r2.exec(s) // ["aaa"]
r1.exec(s) // ["aa"]
r2.exec(s) // null
```
上面代码有两个正则表达式一个使用g修饰符另一个使用y修饰符。这两个正则表达式各执行了两次第一次执行的时候两者行为相同剩余字符串都是“_aa_a”。由于g修饰没有位置要求所以第二次执行会返回结果而y修饰符要求匹配必须从头部开始所以返回null。
如果改一下正则表达式保证每次都能头部匹配y修饰符就会返回结果了。
```javascript
var s = "aaa_aa_a";
var r = /a+_/y;
r.exec(s) // ["aaa_"]
r.exec(s) // ["aa_"]
```
上面代码每次匹配,都是从剩余字符串的头部开始。
进一步说y修饰符号隐含了头部匹配的标志ˆ
```javascript
/b/y.exec("aba")
// null
```
上面代码由于不能保证头部匹配所以返回null。y修饰符的设计本意就是让头部匹配的标志ˆ在全局匹配中都有效。
与y修饰符相匹配ES6的正则对象多了sticky属性表示是否设置了y修饰符。
```javascript
var r = /hello\d/y;
r.sticky // true
```
## 模板字符串
模板字符串template string是增强版的字符串用反引号`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
```javascript
// 普通字符串
`In JavaScript '\n' is a line-feed.`
// 多行字符串
`In JavaScript this is
not legal.`
// 字符串中嵌入变量
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
var x = 1;
var y = 2;
console.log(`${ x } + ${ y } = ${ x + y}`)
// "1 + 2 = 3"
```
上面代码表示,在模板字符串中嵌入变量,需要将变量名写在${}之中。
模板字符串使得字符串与变量的结合,变得容易。下面是一个例子。
```javascript
if (x > MAX) {
throw new Error(`Most ${MAX} allowed: ${x}!`);
// 传统写法为'Most '+MAX+' allowed: '+x+'!'
}
```