ES6对字符串进行了优化改造,支持了更多常用的函数,例如:

  1. 新增了静态方法:String.fromCharCode
  2. 新增了静态方法:String.raw
  3. 新增了实例方法:codePointAt
  4. 新增了实例方法:normalize
  5. 新增了实例方法:includesstartsWithendsWith
  6. 新增了实例方法:repeat
  7. 新增了实例方法:padStartpadEnd
  8. 新增了实例方法:trimStarttrimEnd
  9. 新增了实例方法:matchAll


同时也支持了更多的用法,例如模板字符串:

const value = 123465;
const tmpl = `value = ${value}`;

以及 Unicode 表示法、字符串遍历器等等



静态方法:String.fromCodePoint

这个方法用于从Unicode码点返回对应字符,但是这个方法不能识别码点大于0xFFFF的字符

String.fromCharCode(0x6699) // 暙
String.fromCharCode(0x22336699) // 暙

对于超过0xFFFF的码,则会舍弃最高位的,例如上面的2233就被舍弃掉了

静态方法:String.raw

这个方法用于模板字符串的处理方法,返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串

// 请注意:下面的代码没有打错,没有少括号!!!
String.raw`123\n${1+2+3}\t`
// 实际返回:123\\n6\\t    但运行显示为:123\n6\t

虽然上面展现的方式看起来不像正常的函数,但是String.raw它本质上还是一个正常而又普通的函数,只是它可用于模板字符串的标签函数。如果我们将它写成正常函数的形式,那么它的第一个参数,应该是一个具有raw属性的对象,且raw属性的值应该是一个数组,对应模板字符串解析后的值。

String.raw({ raw: ['123\\n', '\\t'] }, 1 + 2 + 3); // 实际:123\\n6\\t 显示: 123\n6\t
String.raw({ raw: ['abc', '\\t', 'xyz'] }, 1 + 2 + 3); // 实际:abc6\\txyz 显示 abc6\txyz
String.raw({ raw: ['abc', '\\t', 'xyz'] }, 1 + 2 + 3, 456); // 实际:abc6\\t456xyz 显示 abc6\t456xyz

上面的几个例子应该很好理解了

实例方法:codePointAt

JavaScript内部,字符以 UTF-16 的格式储存,每个字符固定为2个字节。对于那些需要4个字节储存的字符(Unicode码点大于0xFFFF的字符),JavaScript 会认为它们是两个字符

cosnt str = '𣌳'; // String.fromCodePoint(0x23333)
str.length // 2
str.charAt(0) // �
str.charAt(1) // �
str.charCodeAt(0) // 55372
str.charCodeAt(0) // 57139

例如,上面的代码,length实际上返回的是2,因为0x23333超过了0xFFFF因此需要使用4个字节储存。对于这种字符,JavaScript不能正确处理,字符串长度会误判为2,而且charAt()方法无法读取整个字符,charCodeAt()方法只能分别返回前两个字节和后两个字节的值,所以ES6提供了codePointAt()方法,它能够正确处理 4 个字节储存的字符,返回一个字符的码点

const str = '𣌳A'; // String.fromCodePoint(0x23333)
str.length // 3
str.codePointAt(0) // 144179
str.codePointAt(1) // 57139
str.codePointAt(2) // 65

你可能注意到了,虽然拿到了正确的码点,但是length返回的字符的长度不对啊,明明是2个字符,却返回了3,因此对于这种情况,我们可以通过ES6提供的解构来解决

const str = '𣌳A'; // String.fromCodePoint(0x23333)
const len = [...str].length // 2

如果需要循环,也可以使用for-each语句来进行循环

const str = '𣌳A'; // String.fromCodePoint(0x23333)
for(let c of str) {
  console.log(c);
}

实例方法:includes、startsWith、endsWith

如果我们想要知道某个字符串中是否包含另一个字符串,ES5中其实只有一个indexOf方法可用(没想到吧~),返回的是以0开头的索引,因此ES6扩展了几个新方法,都返回true/false

includes:返回布尔值,是否包含某个字符串

startsWith:返回布尔值,是否以某个字符串开头

endsWith:返回布尔值,是否以某个字符串结尾

const str = 'hello world';
str.includes('el'); // true
str.startsWith('hello'); // true
str.endWith('world'); // true


实例方法:repeat

这个方法用于将某个字符串循环N次,参数是一个Number,向下取整,如果参数不能转为Number,则返回空字符串

const str = 'miao';
str.repeat(2); // miaomiao
str.repeat(2.88); // miaomiao
str.repeat('2.33'); // miaomiao

str.repeat('0'); // ""
str.repeat('0.99'); // ""
str.repeat('-0.99'); // ""

str.repeat('233ABC'); // ""

请注意,如果参数是负数或是Infinity,会报错,但是-1~0之间的小数会视为0!!!


实例方法:padStart、padEnd

ES2017引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补齐

padStart:用于头部补齐,第一个参数为maxLength,第二个参数为fillString

padEnd:用于尾部补齐,第一个参数为maxLength,第二个参数为fillString

数字补零的必备方法,妈妈再也不用担心我用slice0了~回头还能给萌新小姐姐秀一手,赶紧记住吧~

const val = '998';

val.padStart(6, '0'); // 000998
val.padEnd(6, '0'); // 998000

val.padStart(6, '69'); // 696998
val.padEnd(6, '69'); // 998696


实例方法:trimStart、trimEnd

ES2019对字符串实例新增了trimStarttrimEnd两个方法。它们的作用与trim一致,不过trimStart只会消除字符串头部的空格,trimEnd只会消除字符串尾部的空格,而且它们返回的都是新字符串,不会修改原始字符串。

const str = '  abc  ';

str.trim(); // "abc"
str.trimStart(); // "abc  "
str.trimEnd(); // "  abc"


实例方法:matchAll

这个方法返回一个正则表达式在当前字符串的所有匹配


实例方法:normalize

这个方法不常用(我也没具体用过,所以直接引用 阮老师的博客 吧),用来兼容不同国家的不同的语言文字的。许多欧洲语言有语调符号和重音符号。为了表示它们,Unicode提供了两种方法。一种是直接提供带重音符号的字符,比如Ǒ(\u01D1)。另一种是提供合成符号(combining character),即原字符与重音符号的合成,两个字符合成一个字符,比如O(\u004F)和ˇ(\u030C)合成Ǒ(\u004F\u030C),这两种表示方法,在视觉和语义上都等价,但是 JavaScript 不能识别

'\u01D1'==='\u004F\u030C' //false

'\u01D1'.length // 1
'\u004F\u030C'.length // 2

'\u01D1'.normalize() === '\u004F\u030C'.normalize()




字符串模板

在ES5的时候,如果不借用模板引擎,拼模板字符串的时候大都是使用+号来拼写的,类似于

const tmpl = '小明花了' + val1 + '元钱买了' + val2 + '斤' + val3;

因此ES6新增了模板语法,可以使用`${value}`来简介明了的表示模板

const tmpl = `小明花了${val1}元钱买了${val2}斤${val3}`;

标签模板

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

function compile(tmpl) {
  console.log(tmpl); // [raw: 'hello world', length: 1]
  return tmpl;
}

compile`hello world` // [raw: 'hello world', length: 1]

标签模板其实不是模板,而是函数调用的一种特殊形式,标签指的就是函数,紧跟在后面的模板字符串就是它的参数




字符的 Unicode 表示法

ES6允许采用\uxxxx形式表示一个字符,其中xxxx表示字符的 Unicode 码点,例如使用\u0041表示字符A,但是这种表示法只限于码点在\u0000~\uFFFF之间的字符。超出这个范围的字符,必须用两个双字节的形式表示,例如对于双字节字符𣌳  \u23333则必须使用\ud84c\udf33来表示,如果直接使用\u23333则会展示⌳3, 因此ES6还允许使用大括号来解读双字节字符,例如\u{23333}则会正确被解读

'\ud84c\udf33' === '𣌳' // true
'\u{23333}' === '𣌳' // true

字符串遍历器

ES6为字符串添加了遍历器接口,所以字符串可以被for...of循环遍历

for (let cp of 'XYZ') {
  console.log(cp)
}
// "X"
// "Y"
// "Z"

JSON.stringify 的优化

根据JSON的标准,JSON数据必须是UTF-8编码,由于引入了UTF-16,为了确保返回的是合法的UTF-8字符,ES2019改变了JSON.stringify的行为。如果遇到0xD8000xDFFF之间的单个码点,或者不存在的配对形式,它会返回转义字符串,留给开发者自己决定下一步的处理

JSON.stringify('\u{D666}'); // ""\\uD666""
JSON.stringify('\ud84c\udf33'); // ""\\udf06\\ud834""