在谈及字符串的扩展之前,先说说字符的扩展,ES6中对于字符的扩展,主要就是解决Unicode码点超过0xFFFF的字符在过去的版本中出现的问题,填了之前遍历含Unicode码点超过0xFFFF的字符串时的一些坑。
字符的Unicode表示法
在JavaScript中允许使用\uxxxx表示一个字符,其中xxxx是字符的Unicode码点。但这种方法的码点需要在0000~ffff之间,超出这个范围的字符必须使用两个双字节的形式表示,而且码点如果小于四位的话会报错。
'\u0067'//"g"
'\u67'//Uncaught SyntaxError: Invalid Unicode escape sequence
'\u00677'//"g7"
JavaScript可用于表示字符的方法
'\z' === 'z' // true
'\172' === 'z' // true
'\x7A' === 'z' // true 2位16进制数
'\u007A' === 'z' // true 4位16进制数
'\u{7A}' === 'z' // true
codePointAt
codePointAt方法用于获取字符串中某个位置字符的编码(十进制)
在ES5中,也可以使用charCodeAt来获取字符的编码(十进制),但是对于Unicode编码码点大于0xFFFF的字符,charCOdeAt没法正确识别,而且一个大于0xFFFF的字符在调用字符串的length属性时会被认为是两个长度,这种字符使用cahrAt也同样不能获取到对应的字符。
var s = "?"
s.length // 2
s.charAt(0) // ''
s.charAt(1) // ''
s.charCodeAt(0) // 55362
s.charCodeAt(1) // 57271
?的码点为0x20BB7,大于0xFFFF,其UTF-16 编码为0xD842 0xDFB7(十进制为55362 57271),需要4个字节储存,所以没法使用charAt获取该字符,也没法用charCodeAt来的到其正确的编码。
而通过使用codePointAt,就可以获取对应的编码,而通过使用toString(16),可以转化为16进制的码点。
let s = '?';
s.codePointAt(0) // 134071
s.codePointAt(1) // 57271
s.codePointAt(2) // 97
s.codePointAt(0).toString(16) // "20bb7"
s.codePointAt(2).toString(16) // "61"
虽然可以获取?的对应编码了,但可以注意到,a本来应该放在1的位置,但是却要传入2才能得到a的编码,这是因为JavaScript将码点超过0xFFFF的字符看成两个字符,所以a的位置往后挪了一位。
for...of会正确识别 32 位的 UTF-16 字符,所以可以使用下面的方法来获取正确的字节。
function charPointAt(str,pos){
let index=0;
for(let c of str){
if(index===pos)
return c;
index++;
}
}
charPointAt(s,0);
String.fromCodePoint()
String.fromCodePoint传入码点作为参数,返回对应的字符。ES5中也有String.fromCharCode传入码点返回对应的字符,但是对于码点大于0xFFFF的码点,String.fromCharCode不能返回正确的字符,而String.fromCodePoint正好弥补了这一缺点。同时String.fromCodePoint也可传入多个参数,结果会返回由参数码点对应的字符组成的字符串。
String.fromCharCode(0x20BB7)// "ஷ"
String.fromCodePoint(0x20BB7)// "
String.fromCodePoint(0x78, 0x1f680, 0x79) === 'x\uD83D\uDE80y'// true
说完了字符的扩展,开始说说字符串的扩展
includes(), startsWith(), endsWith()
ES6提供了三个新的判断字符串内是否包含某个字符串的方法
- includes():返回布尔值,表示是否找到了参数字符串。
- startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
- endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
let str="hello world";
str.includes('hello');//true
str.startsWith('hello');//true
str.endsWith('world');//true
这三个方法第一个参数为要查找的字符串,对于includes和startsWith,第二个参数为开始查找的位置,而对于endsWith,是对前n个字符进行查找
repeat
repeat方法用于生成某个字符串重复多次的字符串,传入的参数为重复次数,
"x".repeat(5);//"xxxxx"
"hello".repeat(2);//"hellohello"
"str".repeat(0);//""
传入的参数不是正整数的情况:
浮点数:取向下取整后的数
"x".repeat(2.8);//"xx"
"x".repeat(2.1);//"xx"
字符串:取字符串转为整形的数,若无法转化,则为0
'x'.repeat('3');//"xxx"
'x'.repeat('na');//""
负数:报错(若为-1到0之间的小数,则等同于传入0)
'x'.repeat(-1)//Invalid count value at String.repeat (<anonymous>)
'x'.repeat(-0.4)//""
'x'.repeat(Infinity);//Invalid count value at String.repeat (<anonymous>)
'x'.repeat(NaN);//""
Infinity:报错
NaN:等同于0
padStart,padEnd
padStart,padEnd两个方法用于在字符串头部和尾部用传入的第二个参数补全数组以使长度达到第一个参数的值,若省去第二个参数,则默认使用空格补全,若传入的第一个参数小于或等于原字符串的长度,则返回原字符串,而如果原字符串加上填充字符串大于设定的长度,则截去超过部分的填充字符串。
'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'
'xxx'.padStart(2, 'ab') // 'xxx'
'xxx'.padEnd(2, 'ab') // 'xxx'
'x'.padStart(4) // ' x'
'x'.padEnd(4) // 'x '
String.prototype.matchAll
该方法用于匹配正则表达式并返回一个遍历器
const string = 'test1test2test3';
// g 修饰符加不加都可以
const regex = /t(e)(st(\d?))/g;
for (const match of string.matchAll(regex)) {
console.log(match);
}
// ["test1", "e", "st1", "1", index: 0, input: "test1test2test3"]
// ["test2", "e", "st2", "2", index: 5, input: "test1test2test3"]
// ["test3", "e", "st3", "3", index: 10, input: "test1test2test3"]
模板字符串
在之前的JavaScript中,如果要在一段字符串中间插入一些变量,需要用+连接字符串
let a=1;
let b=2;
console.log("a:"+a+" b:"+b);
一个console语句看起来并不多,但如果要写一段HTML代码的话,看起来就会有点复杂了,如下面的代码插入一段HTML代码
$('#result').append(
'There are <b>' + basket.count + '</b> ' +
'items in your basket, ' +
'<em>' + basket.onSale +
'</em> are on sale!');
而在ES6中提供了模板字符串的写法,模板字符串是写在两个`中间的,其中可以通过使用${}来获取变量的值,同时{}中也可以放入表达式,引用对象的属性以及函数,如果传入的是字符串,则原样输出
$('#result').append(`
There are <b>${basket.count}</b> items
in your basket, <em>${basket.onSale}</em>
are on sale!
`);
要注意的是,如果传入${}的变量没有声明,则会报错
`${a}`//ReferenceError: a is not defined
模板字符串会保留其中的空格和换行,如果不要这个换行,可以调用trim方法去掉
$('#list').html(`
<ul>
<li>first</li>
<li>second</li>
</ul>
`.trim());
表达式作用域
模板字符串中的表达式的作用域在它出现的词法作用域中,没有任何形式的动态作用域。
function f1(str){
var name='f1';
console.log(str);
}
function f2(){
var name='f2';
f1(`name:${name}`);
}
f2()
// name:f2
标签模板
标签模板是模板字符串的一个功能,将模板字符串放在一个函数名后面会调用该函数来处理这个模板字符串
alert`str`
若该模板字符串中没有${},则直接将这个模板字符串当作一个字符串参数传给其前面的函数
如果有${},则第一个参数为以${}为分隔的字符串数组,后面的参数依次为${}中的变量,若${}在最后一个位置,则第一个参数的字符串数组的最后一个成员是""。
function fn(){
for (let i of arguments)
console.log(i);
}
fn`abc${1+2}efg${3+4}`
//["abc", "efg", "", raw: Array(3)]
//3
//7
上面的代码中以${}为分隔,第一个参数为["abc',"efg",""],然后后面的参数为${}中的内容,按顺序作为后面的参数,即3,7。
如果要将这些变量拼合在原位置,可以使用下面的方法来实现
function fn(arr,...rest){
let result="";
let index=0;
let length=rest.length;
for (let i of arr){
let r=index<length?rest[index++]:"";
result=result+i+r;
}
return result;
}
String.raw
处理模板字符串
用String.raw处理模板字符串时,模板字符串中所有斜杠前面再加一个斜杠,${}的表达式运算出结构后添加在对于的位置,最后返回处理好的字符串
String.raw的代码实现如下
String.raw = function (strings, ...values) {
let output = '';
let index;
for (index = 0; index < values.length; index++) {
output += strings.raw[index] + values[index];
}
output += strings.raw[index]
return output;
}
下面看看String.raw对字符串处理的结果
console.log(`hello\nworld`);
console.log(String.raw`hello\nworld`);
console.log(String.raw`hello\nworld`.length);
// hello
world
// hello\nworld
// 12
Unicode标识符名
Unicode也可以用作标识符名(变量,属性等)。在ES6之前,可以使用Unicode转义符实现这一点,如下代码:
var \u03A9=1;
console.log(\u03A9); // 1
//等同于
var Ω=1;
console.log(Ω); // 1
而在ES6中,可以使用码点转义符来做到将超过0xFFFF的字符作为标识符名。如下代码:
var \u{22311}=2;
console.log(\u{22311}); // 2
//等同于
var ?=2;
console.log(?); // 2
参考自阮一峰的《ECMAScript6入门》
Kyle Simpson的《你不知道的JavaScript 下卷》
ES6学习笔记目录(持续更新中)