Appearance
字符串扩展
字符 unicode
字符串遍历接口
ES6 为字符串添加了遍历器接口,使得字符串可以被for...of循环遍历。
js
for (let codePoint of 'foo') {
console.log(codePoint)
}
// "f"
// "o"
// "o"for...of 优势:识别 0xFFFF码点,传统for无法识别
直接输入 U+2028 和 U+2029
JSON.stringify() 的改造
JSON.stringify()的问题在于,它可能返回0xD800到0xDFFF之间的单个码点。
ES2019 改变了JSON.stringify()的行为。如果遇到0xD800到0xDFFF之间的单个码点,或者不存在的配对形式,它会返回转义字符串。
js
// Non-BMP characters still serialize to surrogate pairs.
JSON.stringify('𝌆')
// → '"𝌆"'
JSON.stringify('\uD834\uDF06')
// → '"𝌆"'
// Unpaired surrogate code units will serialize to escape sequences.
JSON.stringify('\uDF06\uD834')
// → '"\\udf06\\ud834"'
JSON.stringify('\uDEAD')
// → '"\\udead"'字符串模板
单双引号字符串:均不解析变量,需要使用 + 号将变量拼接在字符串中
字符串模板(模板字面量):允许使用反引号**``**来创建字符串
空格换行保留
变量写在
${}中,非字符串,如对象,调用toString()可用 Js 表达式,能调用函数
js
function fn() {
return 'Hello World'
}
;`foo ${fn()} bar`
// foo Hello World bar- 支持嵌套
js
const tmpl = (addrs) => `
<table>
${addrs
.map(
(addr) => `
<tr><td>${addr.first}</td></tr>
<tr><td>${addr.last}</td></tr>
`
)
.join('')}
</table>
`- 引用本身,写成函数
js
let func = (name) => `Hello ${name}!`
func('Jack') // "Hello Jack!" 引用本身模板编译
js
let template = `
<ul>
<% for(let i=0; i < data.supplies.length; i++) { %>
<li><%= data.supplies[i] %></li>
<% } %>
</ul>
`将其转换为 javascript 表达式
js
echo('<ul>')
for (let i = 0; i < data.supplies.length; i++) {
echo('<li>')
echo(data.supplies[i])
echo('</li>')
}
echo('</ul>')使用非贪婪匹配的原因:保证 <% 遇见下一个 %>就匹配。
js
function compile(template) {
const evalExpr = /<%=(.+?)%>/g
const expr = /<%([\s\S]+?)%>/g // 非贪婪匹配 +?
template = template.replace(evalExpr, '`); \n echo( $1 ); \n echo(`').replace(expr, '`); \n $1 \n echo(`')
template = 'echo(`' + template + '`);'
let script = `(function parse(data){
let output = "";
function echo(html){
output += html;
}
${template}
return output;
})`
return script
}标签模板
标签模板:函数调用特殊形式
模板字符串前面有一个标识名tag,它是一个函数。整个表达式的返回值,就是tag函数处理模板字符串后的返回值
js
let a = 5
let b = 10
tag`Hello ${a + b} world ${a * b}`
// 等同于
tag(['Hello ', ' world ', ''], 15, 50)参数一:['Hello ', ' world ', '']
参数二:15
参数三:50
js
// 第一个参数为数组,模板字符串中没有被变量替换的部分
// 其他参数为替换变量
function tag(stringArr, value1, value2) {
// ...
// stringArr - ['hello ',' world ','']
// value1 - 15
// value2 - 50
}
// 等同于
function tag(stringArr, ...values) {
// ...
}重要应用:
- 过滤 HTML,防止用户输入恶意内容
- 多语言转换
模板字符串限制
字符串新增方法
String.fromCodePoint()
ES5 - String.fromCharCode():从 Unicode 码点返回字符串。
问题:不能识别大于 0xFFFF 的字符,0x20BBF 舍弃高位变为 0x0BBF
String.fromCodePoint():与codePointAt()相反,多个参数,合并为一个字符串
fromCodePoint定义在 String 对象上,codePointAt定义在字符串对象上
String.raw()
ES6 为原生 String 对象 提供 raw()方法。
作用:返回斜杠都被转义的字符串。将变量替换,对斜杠转义。
js
String.raw`Hi\n${2 + 3}!`
// 实际返回 "Hi\\n5!",显示的是转义后的结果 "Hi\n5!"
// `foo${1 + 2}bar`
// 等同于
String.raw({ raw: ['foo', 'bar'] }, 1 + 2) // "foo3bar"实列方法 - charCodeAt()
ES5 - charAt():从一个字符串中返回指定的字符
ES5 - charCodeAt():方法返回 0 到 65535 之间的整数,表示给定索引处的 UTF-16 代码单元
JavaScript 内部,字符以 UTF-16 的格式储存,每个字符固定为2个字节。
问题:无法解决 4 个字节字符(大于0xFFFF),JavaScript 会认为它们是两个字符。
codePointAt():处理 4 个字节存储的字符
𠮷 - 码点0x20BB7,UTF-16 编码为0xD842 0xDFB7
js
let s = '𠮷a'
s.codePointAt(0) // 134071
s.codePointAt(1) // 57271
s.codePointAt(2) // 97测试一个字符由两个字节还是由四个字节组成
js
function is32Bit(c) {
return c.codePointAt(0) > 0xffff
}
is32Bit('𠮷') // true
is32Bit('a') // falseUnicode 、UTF-8、UTF-8 : 参考末尾文章
Unicode:一个符号集, 它只规定了每个符号的二进制值。将世界各种语言的每个字符定义一个唯一的编码
UTF-8:一种变长字符编码,将码点编码为 1 至 4 个字节,字节数取决于码点数值中有效二进制位的数量
UTF-16:UTF-16 也是一种变长字符编码, 这种编码方式比较特殊, 它将字符编码成 2 字节 或者 4 字节
实例方法 - normalize()
normalize():将字符的不同表示方法统一为同样的形式,这称为 Unicode 正规化
实例方法 - includes(),startsWith(),endsWith()
es5 - indexOf():查找一个字符串是否在里一个字符串中。
问题:不够直观,返回的 number
includes(string, number):找到 string,number 为起始位置
startsWith(string, number):以 string 开头的字符串,number 为起始位置
endsWith(string, number):以 string 结尾的字符串,number 为前 n 个字符
js
let s = 'Hello world!'
s.startsWith('world', 6) // true
s.endsWith('Hello', 5) // true
s.includes('Hello', 6) // false均返回 boolean
实例方法 - repeat()
repeat():返回新字符串,原字符串重复 n 次
- n 为
小数,取整 - n 为
负数,infinity报错 - n 为
-1~0,NaN,均为 0,返回空值
js
'hello'.repeat(2) // "hellohello"
'na'.repeat(0) // ""
'na'.repeat(NaN) // ""js
// 1.小数点取整
'na'.repeat(-0.9) // ""
// 2.负数和 Infinity 报错
'na'.repeat(Infinity) // RangeError
'na'.repeat(-1) // RangeError
// 3.字符串转为数字
'na'.repeat('na') // ""
'na'.repeat('3') // "nanana"实例方法 - padStart(),padEnd()
padStart():头部补全
- 参数 1:补全最大长度
- 参数 2:补全字符串(默认空格)
补全规则
大于等于长度,返回原串
补全长度 + 原串 > 最大长度,截取补全字符串
js
'x'.padStart(5, 'ab') // 'ababx'
'x'.padEnd(5, 'ab') // 'xabab'
'xxx'.padStart(2, 'ab') // 'xxx'
'xxx'.padEnd(2, 'ab') // 'xxx'
'abc'.padStart(10, '0123456789') // '0123456abc'padEnd():尾部补全,同上
用途
- 数值补 0
js
'123456'.padStart(10, '0') // "0000123456"- 字符串格式提示
js
'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"实例方法 - trimStart(),trimEnd()
trimStart():消除头部空格
trimEnd():消除尾部空格
返回新字符串
实例方法 - matchAll()
ES5 - regexp.exec():获取匹配项信息
获取所有匹配项,需要循环
js
const regexp = RegExp('foo[a-z]*', 'g')
const str = 'table football, foosball'
let match
while ((match = regexp.exec(str)) !== null) {
console.log(`Found ${match[0]} start=${match.index} end=${regexp.lastIndex}.`)
// expected output: "Found football start=6 end=14."
// expected output: "Found foosball start=16 end=24."
}ES2020 - matchAll():返回一个正则表达式在当前字符串的所有匹配
js
const regexp = RegExp('foo[a-z]*', 'g')
const str = 'table football, foosball'
const matches = str.matchAll(regexp)
for (const match of matches) {
console.log(`Found ${match[0]} start=${match.index} end=${match.index + match[0].length}.`)
}
// expected output: "Found football start=6 end=14."
// expected output: "Found foosball start=16 end=24."没有
/g标志,matchAll会抛出异常
实例方法 - replaceAll()
ES5 - replace():将匹配的字符串,用指定字符串替换
js
'aabbcc'.replace('b', '_') // 替换第一个匹配
'aabbcc'.replace(/b/g, '_') // 替换所有匹配replaceAll( searchValue, replacement):所有匹配的字符串替换
searchValue - 字符串 or 全局正则表达式(带g修饰符,不带报错)
replacement - 字符串 or 函数,标识替换文本。
- 字符串
$&:匹配的子字符串。- $`:匹配结果前面的文本。
$':匹配结果后面的文本。$n:匹配成功的第n组内容(n 从 1开始),前提是,第一个参数必须是正则表达式。$$:指代美元符号$。
js
// $& 表示匹配的字符串,即`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'- 函数
js
const str = '123abc456'
const regex = /(\d+)([a-z]+)(\d+)/g
/*
params #1 - 匹配内容
params #2 - 捕捉到是组匹配(有多少个组匹配,就有多少个对应的参数)
...
params #n-1 - 捕获内容在字符串位置
parmas #n - 原字符串
*/
function replacer(match, p1, p2, p3, offset, string) {
return [p1, p2, p3].join(' - ')
}
str.replaceAll(regex, replacer)
// 123 - abc - 456实例方法 - at()
at():接受一个整数作为参数,返回参数指定位置的字符。(支持负索引)
js
const str = 'hello'
str.at(1) // "e"
str.at(-1) // "o"
str.at(1111) // undefined超出范围,返回 undefined