ECMAScript 6 Primer II
4 字符串的扩展
4.1 字符的Unicode表示法
JavaScript允许采用\uxxxx
形式表示一个字符,其中xxxx
表示字符的Unicode码点。
但是这种表示法只限于码点在\u0000
~\uFFFF
之间的字符,超出范围的字符必须要用连个双字节的形式表示。
ES6允许将码点放入大括号中,就能够正确解读超过\uFFFF
的字符。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17"\uD842\uDFB7"
// "𠮷"
"\u20BB7"
// " 7" 无法正确解读,会理解\u20BB+7,而\u20BB是一个不可打印字符
"\u{20BB7}"
// "𠮷"
"\u{41}\u{42}\u{43}"
// "ABC"
let hello = 123;
hell\u{6F} // 123
'\u{1F680}' === '\uD83D\uDE80'
// true
4.2 codePointAt()
JavaScript内部字符以UTF-16格式存储,每个字符固定为2个字节,对于需要使用4个字节存储的字符(即码点大于0xFFFF
),JavaScript会认为他们是两个字符。
ES6提供了codePointAt
方法,能够正确处理4个字节存储的字符,返回一个字符的码点。
4.3 String.fromCodePoint()
ES6提供了String.fromCodePoint
方法,可以识别大于0xFFFF
的字符,弥补了String.fromCharCode
方法的不足。
4.4 字符串的遍历接口
ES6为字符串添加了遍历器接口,使得字符串可以被for ... of
循环遍历。1
2
3
4
5
6for (let codePoint of 'foo') {
console.log(codePoint)
}
// "f"
// "o"
// "o"
这个遍历器的最大优点是可以识别大于0xFFFF
的码点。
4.5 at()
ES5对字符串对象提供charAt
方法,返回字符串给定位置的字符,但是该方法不能识别码点大于0xFFFF
的字符。
目前有一个提案,提出字符串实例的at
方法,可以识别大于0xFFFF
的字符。
4.6 normalize()
ES6提供字符串实例的normalize
方法,用来将字符的不同表示方法统一为同样的形式,成为Unocode正规化。
主要用于在判断欧洲语言有语调符号和重音符号的字符在视觉和语义上的判断是等价的。
4.7 includes(), startsWith(), endsWith()
除了传统的indexOf
方法,ES6又提供了三种方法来确定一个字符串是否包含在另一个字符串中。
- includes(): 返回布尔值,表示是否找到了参数字符串
- startsWith(): 返回布尔值,表示参数字符串是否在原字符串的头部
- endsWith(): 返回布尔值,表示参数字符串是否在原字符串的尾部
三个方法都支持第二个参数,前两个表示开始搜索的位置,最后一个表示针对前n
个字符。
4.8 repeat()
repeat
方法返回一个新字符串,表示将原字符串重复n
次。1
2'x'.repeat(3) // "xxx"
'na'.repeat(0) // ""
4.9 padStart(), padEnd()
如果某个字符串不够指定长度,ES2017提供了在头部或尾部补全的方法。padStart()
用于头部补全,padEnd()
用于尾部补全。1
2
3
4
5'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'
4.10 matchAll()
matchAll
方法返回一个正则表示式在当前字符串的所有匹配,详见《正则的扩展》一节。
4.11 模板字符串
ES6引入了模板字符串,是增强版的字符串,用反引号标识,可以定义多行字符串,或者嵌入变量。1
2
3
4
5
6
7
8
9
10
11
12
13// 普通字符串
`In JavaScript '\n' is a line-feed.`
// 多行字符串
`In JavaScript this is
not legal.`
console.log(`string text line 1
string text line 2`);
// 字符串中嵌入变量
let name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
实际上大括号内部可以放入任意的JavaScript表达式,可以进行运算,引用对象属性,调用函数等。
如果需要引用模板字符串本上,在需要时执行,可以像下面这样写。1
2
3
4
5
6
7
8
9// 写法一
let str = 'return ' + '`Hello ${name}!`';
let func = new Function('name', str);
func('Jack') // "Hello Jack!"
// 写法二
let str = '(name) => `Hello ${name}!`';
let func = eval.call(null, str);
func('Jack') // "Hello Jack!"
4.12 实例:模板编译
具体请参见这里。
4.13 标签模板
模板字符串还可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。着被称为“标签模板”功能(tagged template)。1
2
3alert`123`
// 等同于
alert(123)
4.13 String.raw()
ES6还未原生的String对象提供了一个raw
方法,用来充当模板字符床的处理函数,返回一个斜杠都被转义的字符串。1
2
3
4
5String.raw`Hi\n${2+3}!`;
// 返回 "Hi\\n5!"
String.raw`Hi\u000A!`;
// 返回 "Hi\\u000A!"
如果原字符串的斜杠已经转义,那么String.raw
会进行再次转义。
5 正则的扩展
5.1 RegExp构造函数
ES6允许在使用RegExp
构造函数时,当第一个参数是正则表达式时,可以使用第二个参数添加修饰符。
5.2 字符串的正则方法
ES6将字符串对象的4个正则表达式:match()
、replace()
、search()
和split()
,在语言内部全部调用RegExp
的实例方法。
5.3 u修饰符
ES6对正则表达式添加了u
修饰符,含义为“Unicode模式”,用来正确处理四个字节的UTF-16编码。
详细参见这里。
5.4 y修饰符
y
修饰符叫做“粘连”修饰符,表示确保匹配必须从剩余的第一个位置开始。
5.5 sticky属性
与y
修饰符相匹配,ES6的正则对象多了sticky
属性,表示是否设置了y
修饰符。
5.6 flags属性
ES6为正则表达式新增了flags
属性,返回正则表达式的修饰符。
5.7 s修饰符:dotAll模式
正则表达式中,点(.)是一个特殊字符,代表任意的单个字符,但是有两个例外。一个是四个字节的 UTF-16 字符,这个可以用u修饰符解决;另一个是行终止符(line terminator character)。
ES2018 引入s修饰符,使得.可以匹配任意单个字符。
5.8 后行断言
ES2018引入了后行断言。
5.9 Unicode属性类
ES2018引入了一种新的类的写法\p{...}
和\P{...}
,允许正则表达式匹配符合Unicode某种属性的所有字符。
5.10 具名组匹配
正则表达式时用圆括号进行组匹配。ES2018引入了具名组匹配(Named Capture Groups),允许为每一个组匹配指定一个名字,既便于阅读代码,又便于引用。
有了具名组匹配以后,可以使用解构赋值直接从匹配结果上位变量赋值。1
2
3let {groups: {one, two}} = /^(?<one>.*):(?<two>.*)$/u.exec('foo:bar');
one // foo
two // bar
字符串替换时,使用$<组名>
引用具名组。1
2
3
4let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
'2015-01-02'.replace(re, '$<day>/$<month>/$<year>')
// '02/01/2015'
如果要在正则表达式内部引用某个“具名组匹配”,可以使用\k<组名>的写法。1
2
3const RE_TWICE = /^(?<word>[a-z]+)!\k<word>$/;
RE_TWICE.test('abc!abc') // true
RE_TWICE.test('abc!ab') // false
5.11 String.prototype.matchAll
目前有一个提案,增加了String.prototype.matchAll
方法,可以一次性取出所有匹配。不过,它返回的是一个遍历器(Iterator),而不是数组。
6 熟知的扩展
6.1 二进制和八进制表示法
ES6提供了二进制和八进制熟知的新的表示法,分别用前缀0b
和0o
表示。
6.2 Number.isFinite(), Number.isNaN()
ES6在Number
对象上,新提供了Number.isFinite()
和Number.isNaN()
两个方法。
前者用来检查一个数值是否为有限,既不是Infinity
,后者用来检查一个值是否为NaN
。
6.3 Number.parseInt(), Number.parseFloat()
ES6将全局方法parseInt()
和parseFloat()
,移植到Number
对象上面,行为完全保持不变。
6.4 Number.isInteger()
Number.isInteger()
用来判断一个数值是否为整数。1
2Number.isInteger(25) // true
Number.isInteger(25.0) // true
6.5 Number.EPSILON
ES6在Number
对象上,新增了一个极小的常量Number.EPSILON
,表示1月1的最小浮点数之间的差,实际上是JavaScript能够表示的最小精度。1
2
3
4
5
6
7
8
9function withinErrorMargin (left, right) {
return Math.abs(left - right) < Number.EPSILON * Math.pow(2, 2);
}
0.1 + 0.2 === 0.3 // false
withinErrorMargin(0.1 + 0.2, 0.3) // true
1.1 + 1.3 === 2.4 // false
withinErrorMargin(1.1 + 1.3, 2.4) // true
上面的代码为浮点数运算部署了一个误差检查函数。
6.6 安全整数和Number.isSafeInteger()
ES6引入了Number.MAX_SAFE_INTEGER
和Number.MIN_SAFE_INTEGER
两个常量用来表示整数的上下限。Number.isSafeInteger
则用来判断一个整数是否落在这个范围内。
6.7 Math对象的扩展
Math.trunc()
用于去除一个数的小数部分,返回整数部分。
Math.sign()
用来判断一个数到底是整数、负数、还是零,对于非数值,会先将其转换为数值。它回返回五种植。
- 参数为正数,返回+1;
- 参数为负数,返回-1;
- 参数为 0,返回0;
- 参数为-0,返回-0;
- 其他值,返回NaN。
Math.cbrt()
用于计算一个数的立方根。
Math.clz32()
返回一个数的32为无符号整数形式有多少个前导0。
Math.imul()
返回两个数以32位带符号整数形式相乘的结果,返回的也是一个32为带符号整数。
Math.fround()
返回一个数的32位单精度浮点数形式。
Math.hypot()
返回所有参数的平方和的平方根。
Mach.expm1()
返回ex-1。
Math.log1p()
返回1 + x
的自然对数。
Math.log10()
返回以10为底的x
的对数。
Math.log2()
返回以2为底的x
的对数。
双曲函数
- Math.sinh(x) 返回x的双曲正弦(hyperbolic sine)
- Math.cosh(x) 返回x的双曲余弦(hyperbolic cosine)
- Math.tanh(x) 返回x的双曲正切(hyperbolic tangent)
- Math.asinh(x) 返回x的反双曲正弦(inverse hyperbolic sine)
- Math.acosh(x) 返回x的反双曲余弦(inverse hyperbolic cosine)
- Math.atanh(x) 返回x的反双曲正切(inverse hyperbolic tangent
6.8 指数运算符
ES2016新增了一个指数运算符(**
)。