ES6 字符串的新增方法

Posted 天界程序员

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ES6 字符串的新增方法相关的知识,希望对你有一定的参考价值。

1. 字符串的新增方法

1.1 String.fromCodePoint()

  • String.fromCharCode()

    ES5 提供的方法:

    作用:从 Unicode 码点返回对应字符,范围在0x0000~0xFFFF

    局限:不能识别码点大于0xFFFF的字符,大于0xFFFF的数字将被截断。

    语法String.fromCharCode(num1[, ...[, numN]])

    注意:该方法是一个静态方法,返回值是字符串,而不是一个String对象。

    String.fromCharCode(65, 66, 67);   // "ABC"
    String.fromCharCode(0x20BB7); // "ஷ"
    
  • String.fromCodePoint()

    ES6 中的方法:
    作用:从 Unicode 码点返回对应字符。

    局限:不能识别码点大于0xFFFF的字符,大于0xFFFF的数字将被截断。

    语法String.fromCodePoint(num1[, ...[, numN]])

    注意:该方法是一个静态方法,返回值是字符串,而不是一个String对象。如果传入无效的 Unicode 编码,将会抛出一个RangeError (例如: “RangeError: NaN is not a valid code point”)。

    String.fromCodePoint(0x20BB7)
    // "𠮷"
    
    String.fromCodePoint(0x78, 0x1f680, 0x79) === 'x\\uD83D\\uDE80y'
    // true
    
    String.fromCodePoint('_');      // RangeError
    String.fromCodePoint(Infinity); // RangeError
    String.fromCodePoint(-1);       // RangeError
    String.fromCodePoint(3.14);     // RangeError
    String.fromCodePoint(3e-2);     // RangeError
    String.fromCodePoint(NaN);      // RangeError
    

1.2 String.raw()

  作用:是一个模板字符串的标签函数,是用来获取一个模板字符串的原始字符串的。比如说,占位符(例如 ${foo})会被处理为它所代表的其他字符串,而转义字符(例如 \\n)不会。
  该方法返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串,往往用于模板字符串的处理方法。

  语法String.raw(callSite, ...substitutions)String.raw`templateString`

参数:callSite
  一个模板字符串的“调用点对象”。类似{ raw: [‘foo’, ‘bar’, ‘baz’] }。

参数:…substitutions
   任意个可选的参数,表示任意个内插表达式对应的值。


参数:templateString
  模板字符串,可包含占位符(${…})

  注意:如果第一个参数没有传入一个格式正确的对象,则会抛出 TypeError 异常。

String.raw`Hi\\n${2+3}!`
// 实际返回 "Hi\\\\n5!",显示的是转义后的结果 "Hi\\n5!"

String.raw`Hi\\u000A!`;
// 实际返回 "Hi\\\\u000A!",显示的是转义后的结果 "Hi\\u000A!"

如果原字符串的斜杠已经转义,那么String.raw()会进行再次转义。

String.raw`Hi\\\\n`
// 返回 "Hi\\\\\\\\n"

String.raw`Hi\\\\n` === "Hi\\\\\\\\n" // true

1.3 实例方法:codePointAt()

   作用:返回 一个 Unicode 编码点值的非负整数。

   语法str.codePointAt(index)

index
  这个字符串中需要转码的元素的位置。

   注意:返回值是在字符串中的给定索引的编码单元体现的数字,如果在索引处没找到元素则返回 undefined

'ABC'.codePointAt(1);          // 66
'\\uD800\\uDC00'.codePointAt(0); // 65536

'XYZ'.codePointAt(42); // undefined

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

var s = "𠮷";

s.length // 2
s.charAt(0) // ''
s.charAt(1) // ''
s.charCodeAt(0) // 55362
s.charCodeAt(1) // 57271

上面代码中,汉字“𠮷”(注意,这个字不是“吉祥”的“吉”)的码点是0x20BB7,UTF-16 编码为0xD842 0xDFB7(十进制为55362 57271),需要4个字节储存。对于这种4个字节的字符,JavaScript 不能正确处理,字符串长度会误判为2,而且charAt()方法无法读取整个字符,charCodeAt()方法只能分别返回前两个字节和后两个字节的值。

ES6 提供了codePointAt()方法,能够正确处理 4 个字节储存的字符,返回一个字符的码点。

let s = '𠮷a';

s.codePointAt(0) // 134071
s.codePointAt(1) // 57271

s.codePointAt(2) // 97

codePointAt()方法的参数,是字符在字符串中的位置(从 0 开始)。上面代码中,JavaScript 将“𠮷a”视为三个字符,codePointAt 方法在第一个字符上,正确地识别了“𠮷”,返回了它的十进制码点 134071(即十六进制的20BB7)。在第二个字符(即“𠮷”的后两个字节)和第三个字符“a”上,codePointAt()方法的结果与charCodeAt()方法相同。

总之,codePointAt()方法会正确返回 32 位的 UTF-16 字符的码点。对于那些两个字节储存的常规字符,它的返回结果与charCodeAt()方法相同。

codePointAt()方法返回的是码点的十进制值,如果想要十六进制的值,可以使用toString()方法转换一下。

let s = '𠮷a';

s.codePointAt(0).toString(16) // "20bb7"
s.codePointAt(2).toString(16) // "61"

你可能注意到了,codePointAt()方法的参数,仍然是不正确的。比如,上面代码中,字符a在字符串s的正确位置序号应该是 1,但是必须向codePointAt()方法传入 2。解决这个问题的一个办法是使用for...of循环,因为它会正确识别 32 位的 UTF-16 字符。

let s = '𠮷a';
for (let ch of s) {
  console.log(ch.codePointAt(0).toString(16));
}
// 20bb7
// 61

另一种方法也可以,使用扩展运算符(...)进行展开运算。

let arr = [...'𠮷a']; // arr.length === 2
arr.forEach(
  ch => console.log(ch.codePointAt(0).toString(16))
);
// 20bb7
// 61

codePointAt()方法是测试一个字符由两个字节还是由四个字节组成的最简单方法。

function is32Bit(c) {
  return c.codePointAt(0) > 0xFFFF;
}

is32Bit("𠮷") // true
is32Bit("a") // false

1.4 实例方法:normalize()

   作用:按照指定的一种 Unicode 正规形式将当前字符串正规化。(如果该值不是字符串,则首先将其转换为一个字符串)。

   语法str.normalize([form])

form 可选
四种 Unicode 正规形式(Unicode Normalization Form)"NFC""NFD""NFKC",或 "NFKD" 其中的一个, 默认值为"NFC"

   注意:如果给 form 传入了上述四个字符串以外的参数,则会抛出 RangeError 异常。

许多欧洲语言有语调符号和重音符号。为了表示它们,Unicode 提供了两种方法。一种是直接提供带重音符号的字符,比如Ǒ(\\u01D1)。另一种是提供合成符号(combining character),即原字符与重音符号的合成,两个字符合成一个字符,比如O(\\u004F)和ˇ(\\u030C)合成Ǒ(\\u004F\\u030C)。

这两种表示方法,在视觉和语义上都等价,但是 JavaScript 不能识别。

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

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

上面代码表示,JavaScript 将合成字符视为两个字符,导致两种表示方法不相等。

ES6 提供字符串实例的normalize()方法,用来将字符的不同表示方法统一为同样的形式,这称为 Unicode 正规化。

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

normalize方法可以接受一个参数来指定normalize的方式,参数的四个可选值如下。

  • NFC,默认参数,表示“标准等价合成”(Normalization Form Canonical Composition),返回多个简单字符的合成字符。所谓“标准等价”指的是视觉和语义上的等价。
  • NFD,表示“标准等价分解”(Normalization Form Canonical Decomposition),即在标准等价的前提下,返回合成字符分解的多个简单字符。
  • NFKC,表示“兼容等价合成”(Normalization Form Compatibility Composition),返回合成字符。所谓“兼容等价”指的是语义上存在等价,但视觉上不等价,比如“囍”和“喜喜”。(这只是用来举例,normalize方法不能识别中文。)
  • NFKD,表示“兼容等价分解”(Normalization Form Compatibility Decomposition),即在兼容等价的前提下,返回合成字符分解的多个简单字符。
'\\u004F\\u030C'.normalize('NFC').length // 1
'\\u004F\\u030C'.normalize('NFD').length // 2

上面代码表示,NFC参数返回字符的合成形式,NFD参数返回字符的分解形式。

不过,normalize方法目前不能识别三个或三个以上字符的合成。这种情况下,还是只能使用正则表达式,通过 Unicode 编号区间判断。


1.5 实例方法:includes(), startsWith(), endsWith()

  • includes()

   作用:用于判断一个字符串是否包含在另一个字符串中,根据情况返回 truefalse

   语法str.includes(searchString[, position])

searchString
   要在此字符串中搜索的字符串。
position 可选
   从当前字符串的哪个索引位置开始搜寻子字符串,默认值为 0

   注意includes() 方法是区分大小写的。例如,下面的表达式会返回 false

'Big Letter'.includes('big'); // 返回:false
  • startsWith()

   作用:用来判断当前字符串是否以另外一个给定的子字符串开头,并根据判断结果返回 truefalse

   语法str.startsWith(searchString[, position])

searchString
  要搜索的子字符串。
position 可选
  在 str 中搜索 searchString 的开始位置,默认值为 0

   注意:这个方法区分大小写。

const str1 = 'Saturday night plans';

console.log(str1.startsWith('Sat')); // true
console.log(str1.startsWith('SAt')); // false
  • endsWith()

   作用:用来判断当前字符串是否是以另外一个给定的子字符串“结尾”的,根据判断结果返回 truefalse

   语法str.endsWith(searchString[, length])

searchString
  要搜索的子字符串。
length 可选
  作为 str 的长度。默认值为 str.length

   注意:这个方法也是大小写区分敏感的。

const str1 = 'Cats are the best!';

console.log(str1.endsWith('best', 17)); // true
console.log(str1.endsWith('Best', 17)); // false

  传统上,JavaScript 只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6 又提供了三种新方法。


1.6 实例方法:repeat()

   作用:构造并返回一个新字符串,该字符串包含被连接在一起的指定数量的字符串的副本。表示将原字符串重复n次。

   语法str.repeat(count)

count
介于 0+Infinity (正无穷)之间的整数。表示在新构造的字符串中重复了多少遍原字符串。

   注意:重复次数不能为负数,重复次数必须小于 infinity,且长度不会大于最长的字符串,否则会报错。

"abc".repeat(-1)     // RangeError: repeat count must be positive and less than inifinity
"abc".repeat(0)      // ""
"abc".repeat(1)      // "abc"
"abc".repeat(2)      // "abcabc"
"abc".repeat(3.5)    // "abcabcabc" 参数count将会被自动转换成整数.
"abc".repeat(1/0)    // RangeError: repeat count must be positive and less than inifinity

但是,如果参数是 0 到-1 之间的小数,则等同于 0,这是因为会先进行取整运算。0 到-1 之间的小数,取整以后等于-0repeat视同为 0。

'na'.repeat(-0.9) // ""

参数NaN等同于 0。

'na'.repeat(NaN) // ""

如果repeat的参数是字符串,则会先转换成数字。

'na'.repeat('na') // ""
'na'.repeat('3') // "nanana"

1.7 实例方法:padStart(),padEnd()

  • padStart()

   作用:用另一个字符串填充当前字符串(如果需要的话,会重复多次),以便产生的字符串达到给定的长度。从当前字符串的左侧开始填充。

   语法str.padEnd(targetLength [, padString])

targetLength
   当前字符串需要填充到的目标长度。如果这个数值小于当前字符串的长度,则返回当前字符串本身。
padString 可选
   填充字符串。如果字符串太长,使填充后的字符串长度超过了目标长度,则只保留最左侧的部分,其他部分会被截断。此参数的缺省值为 " "(U+0020)。

'abc'.padStart(10);         // "       abc"
'abc'.padStart(10, "foo");  // "foofoofabc"
'abc'.padStart(6,"123465"); // "123abc"
'abc'.padStart(8, "0");     // "00000abc"
'abc'.padStart(1);          // "abc"
  • padEnd()

   作用:会用一个字符串填充当前字符串(如果需要的话则重复填充),返回填充后达到指定长度的字符串。从当前字符串的末尾(右侧)开始填充。

   语法str.padEnd(targetLength [, padString])

targetLength
   当前字符串需要填充到的目标长度。如果这个数值小于当前字符串的长度,则返回当前字符串本身。
padString 可选
   填充字符串。如果字符串太长,使填充后的字符串长度超过了目标长度,则只保留最左侧的部分,其他部分会被截断。此参数的缺省值为 " "(U+0020)。

'abc'.padEnd(10);          // "abc       "
'abc'.padEnd(10, "foo");   // "abcfoofoof"
'abc'.padEnd(6, "123456"); // "abc123"
'abc'.padEnd(1);           // "abc"

如果用来补全的字符串与原字符串,两者的长度之和超过了最大长度,则会截去超出位数的补全字符串。

'abc'.padStart(10, '0123456789')
// '0123456abc'

如果省略第二个参数,默认使用空格补全长度。

'x'.padStart(4) // '   x'
'x'.padEnd(4) // 'x   '

padStart()的常见用途是为数值补全指定位数。下面代码生成 10 位的数值字符串。

'1'.padStart(10, '0') // "0000000001"
'12'.padStart(10, '0') // "0000000012"
'123456'.padStart(10, '0') // "0000123456"

另一个用途是提示字符串格式。

'12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"

1.8 实例方法:trimStart(),trimEnd()

  • trimStart()

   作用:从字符串的开头删除空格。trimLeft()是此方法的别名。

   语法str.trimStart()str.trimLeft()

   注意trimStart() / trimLeft() 方法移除原字符串左端的连续空白符并返回一个新字符串,并不会直接修改原字符串本身。

var str = "   foo  ";

console.log(str.length); // 8

str = str.trimStart()    // 等同于 str = str.trimLeft();
console.log(str.length); // 5
console.log(str);        // "foo  "

  • trimEnd()

   作用:从一个字符串的末端移除空白字符。trimRight() 是这个方法的别名。

   语法str.trimEnd()str.trimRight()

   注意trimEnd() / trimRight()方法移除原字符串右端的连续空白符并返回,trimEnd() / trimRight()方法并不会直接修改原字符串本身。

var str = "   foo  ";

alert(str.length); // 8

str = str.trimRight();  // 或写成str = str.trimEnd();
console.log(str.length); // 6
console.log(str);       // '   foo'

  除了空格键,这两个方法对字符串头部(或尾部)的 tab 键、换行符等不可见的空白符号也有效。


1.9 实例方法:matchAll()

   作用:返回一个包含所有匹配正则表达式的结果及分组捕获组的迭代器。

   语法str.matchAll(regexp)

regexp
  正则表达式对象。如果所传参数不是一个正则表达式对象,则会隐式地使用 new RegExp(obj) 将其转换为一个 RegExp

   注意RegExp必须是设置了全局模式g的形式,否则会抛出异常TypeError。

const regexp = /t(e)(st(\\d?))/g;
const str = 'test1test2';

const array = [...str.matchAll(regexp)];

console.log(array[0]);
// expected output: Array ["test1", "e", "st1", "1"]

console.log(array[1]);
// expected output: Array ["test2", "e", "st2", "2"]

1.10 实例方法:replaceAll()

   作用:返回一个新字符串,新字符串所有满足 pattern的部分都已被replacement 替换。pattern可以是一个字符串或一个 RegExpreplacement可以是一个字符串或一个在每次匹配被调用的函数。原始字符串保持不变。

   语法const newStr = str.replaceAll(regexp|substr, newSubstr|function)

   注意:当使用一个 regex时,您必须设置全局(“ g”)标志,否则,它将引发 TypeError:“必须使用全局 RegExp 调用 replaceAll”。

'aabbcc'.replaceAll(/b/, '.');
TypeError: replaceAll must be called with a global RegExp


'aabbcc'.replaceAll(/b/g, '.');
"aa..cc"

原来的字符串的实例方法replace()只能替换第一个匹配。

'aabbcc'.replace('b', '_')
// 'aa_bcc'

上面例子中,replace()只将第一个b替换成了下划线。

如果要替换所有的匹配,不得不使用正则表达式的g修饰符。

'aabbcc'.replace(/b/g, '_')
// 'aa__cc'

正则表达式毕竟不是那么方便和直观,ES2021 引入了replaceAll()方法,可以一次性替换所有匹配。

'aabbcc'.replaceAll('b', '_')
// 'aa__cc'

它的用法与replace()相同,返回一个新字符串,不会改变原字符串。

String.prototype.replaceAll(searchValue, replacement)

上面代码中,searchValue是搜索模式,可以是一个字符串,也可以是一个全局的正则表达式(带有g修饰符)。

如果searchValue是一个不带有g修饰符的正则表达式,replaceAll()会报错。这一点跟replace()不同。

// 不报错
'aabbcc'.replace(/b/, '_')

// 报错
'aabbcc'.replaceAll(/b/, '_')

上面例子中,/b/不带有g修饰符,会导致replaceAll()报错。

replaceAll()的第二个参数replacement是一个字符串,表示替换的文本,其中可以使用一些特殊字符串。

  • $&:匹配的子字符串。
  • $`:匹配结果前面的文本。
  • $':匹配结果后面的文本。
  • $n:匹配成功的第n组内容,n是从1开始的自然数。这个参数生效的前提是,第一个参数必须是正则表达式。
  • $$:指代美元符号$

下面是一些例子。

// $& 表示匹配的字符串,即`b`本身
// 所以返回结果与原字符串一致
'abbc'.replaceAll('b', '$&')
// 'abbc'

// $` 表示匹配结果之前的字符串
// 对于第一个`b`,$` 指代`a`
// 对于第二个`b`,$` 指代`ab`
'abbc'.replaceAll('b', '$`')
// 'aaabc'

// $' 表示匹配结果之后的字符串
// 对于第一个`b`,$' 指代`bc`
// 对于第二个`b`,$' 指代`c`
'abbc'.replaceAll('b', `$'`)
// 'abccc'

// $1 表示正则表达式的第一个组匹配,指代`ab`
// $2 表示正则表达式的第二个组匹配,指代`bc`
'abbc'.replaceAll(/(ab)(bc)/g, '$2$1')
// 'bcab'

// $$ 指代 $
'abc'.replaceAll('b', '$$')
// 'a$c'

replaceAll()的第二个参数replacement除了为字符串,也可以是一个函数,该函数的返回值将替换掉第一个参数searchValue匹配的文本。

'aabbcc'.replaceAll('b', () => '_')
// 'aa__cc'

上面例子中,replaceAll()的第二个参数是一个函数,该函数的返回值会替换掉所有b的匹配。

这个替换函数可以接受多个参数。第一个参数是捕捉到的匹配内容,第二个参数捕捉到是组匹配(有多少个组匹配,就有多少个对应的参数)。此外,最后还可以添加两个参数,倒数第二个参数是捕捉到的内容在整个字符串中的位置,最后一个参数是原字符串。

const str = '123abc456';
const regex = /(\\d+)([a-z]+)(\\d+)/g;

function replacer(match, p1, p2, p3, offset, string) {
  return [p1, p2, p3].join(' - ');
}

str.replaceAll(regex, replacer)
// 123 - abc - 456

上面例子中,正则表达式有三个组匹配,所以replacer()函数的第一个参数match是捕捉到的匹配内容(即字符串123abc456),后面三个参数p1p2p3则依次为三个组匹配。

以上是关于ES6 字符串的新增方法的主要内容,如果未能解决你的问题,请参考以下文章

javascript es6新增语法之`${}`

ES6学习—字符串的新增方法

ES6 第四章 字符串的新增方法

ES6 第四章 字符串的新增方法

ES6语法总结-字符串新增方法

以下关于array的方法,哪个属于es6新增的