字符串的扩展
几个用于字符串处理的函数
字符串查询
indexOf()返回数字,表示参数在字符串中的位置,如没有,就返回-1。includes()返回布尔值,表示是否找到了参数字符串。startsWith()返回布尔值,表示参数字符串是否在原字符串的头部。endsWith()返回布尔值,表示参数字符串是否在原字符串的尾部。
let str = 'abcdefg'
str.indexOf('c') //2
str.indexOf('x') // -1
str.indexOf('a', 1) // -1
str.indexOf('c', 1) // 2
str.includes('a') // true
str.includes('a', 1) // false
str.includes('c', 1) // true
str.startsWith('a') // true
str.startsWith('b') // false
str.startsWith('b', 1) // true
str.startsWith('a', 1) // false
str.endsWith('g') // true
str.endsWith('f') // false
str.endsWith('g', 3) // false
str.endsWith('c', 3) // true
repeat()
repeat方法返回一个新字符串,表示将原字符串重复n次。
'ab'.repeat(3) // "ababab"
'ab'.repeat(0) // ""
padStart(),padEnd()
ES2017 引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全。
padStart 和 padEnd 有两个参数
第一个参数:用来指定字符串的最小长度
第二个参数:是用来补全的字符串的
'abc'.padStart(5, 'x') // xxabc
'abc'.padEnd(5, 'x') // abcxx
- 如果原字符串的长度,等于或大于指定的最小长度,则返回原字符串。
- 如果用来补全的字符串与原字符串,两者的长度之和超过了指定的最小长度,则会截去超出位数的补全字符串。
- 如果省略第二个参数,默认使用空格补全长度。
'abc'.padStart(2, 'x') // abc
'abc'.padEnd(2, 'x') // abc
'abc'.padStart(10, '0123456789') // '0123456abc'
'x'.padStart(4) // ' x'
'x'.padEnd(4) // 'x '
应用
// 为数值补全指定位数。
'1'.padStart(10, '0') // "0000000001"
// 提示字符串格式
'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"
模板字符串
字符串不使用引号括起来,而使用反引号(``)括起来。
基本用法:
// 普通字符串
`this is a str`
// 多行字符串
`this is a
multi-line. sentence`
// 字符串中嵌入变量
var name = "Li", age = 10;
`i'm ${name}, I'm ${age + 1} years old`
有以下特点:
- 可以当普通字符串用。普通字符串有的功能模板字符串都有。
- 可以用来定义多行字符串而无需写换行符,只要原样输入即可。所有的空格和缩进都会被保留在输出之中。
- 可以嵌入变量,表达式。(另一个模板字符串也是变量,所以模板字符串是可以层层嵌套的)
当前入的变量或表达的结果不是字符串时会自动转换成字符串:
// undefined
`echo ${undefined} !` // 'echo undefined !'
// null
`echo ${null}` // 'echo null !'
// object
let obj = { a: 1 }
`echo ${obj} !` // 'echo [object Object] !'
// array
let arr = ['a', 'b']
`echo ${arr} !` // 'echo a,b !'
//number
let num = 123
`echo ${num} !` // 'echo 123 !'
标签模板
模板字符串可以紧跟在函数后,让函数来处理这个字符串。这被称为“标签模板”功能(tagged template)。
alert`123`
// 等同于
alert(123)
标签模板是函数调用的一种特殊形式。“标签”指的就是函数,紧跟在后面的模板字符串就是它的参数。
如果模板字符里面有变量,会将模板字符串先处理成多个参数,再调用函数。
let xx = '你好'
console.log`Hello ${xx} world ${ 10 } !`
console.log(['Hello', 'world', '!'], xx, 10)
模板字符串 会以变量或表达式为分隔符分割。
- 字符串部分变为一个数组,
- 变量部分变为多个变量,成为参数 生成的数组会拥有一个 raw 属性,raw 的值 就是该数值成员的转义后的 组成的数组。
const fun = (arr1, ...arr2) => {
console.log(arr1)
console.log(arr2)
}
const fun = (arr1, param1, param2, param3....) => {
...
}
fun`this ${'sentence'} \n is ${'xxx'}`
这是为了方便取得转义之前的原始模板而设计的。
模板标签的应用:
1. 重组字符串,过滤 HTML 字符串,防止用户输入恶意内容。
function SaferHTML(strs, ...pars) {
let str = strs[0]
for (let i = 1; i < strs.length; i++) {
let param = String(pars[i - 1])
str += param.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
str += strs[i]
}
return str;
}
let name = 'Tim'
let message = "<script> alert('x') </script>"
SaferHTML`@${name} said: ${message}`
2. 多语言转换
const i18n = (strs, ...pars) => {
return `欢迎访问${pars[0]},您是第${pars[1]}位访问者!`
}
i18n`Welcome to ${'siteName'}, you are visitor number ${'visitorNumber'}!`
// "欢迎访问xxx,您是第xxxx位访问者!"
3. 使用标签模板,在 JavaScript 语言之中嵌入其他语言。
jsx`
<div>
<input
ref='input'
onChange='${this.handleChange}'
defaultValue='${this.state.value}' />
${this.state.value}
</div>
`
上面的代码通过jsx函数,将一个 DOM 字符串转为 React 对象。
String.raw()
String.raw方法,返回一个被转义的字符串。
String.raw`Hi\n${2+3}!`;
// "Hi\\n5!"
String.raw`Hi\u000A!`;
// 'Hi\\u000A!'
如果原字符串的斜杠已经转义,那么String.raw不会做任何处理。
String.raw方法也可以作为正常的函数使用。这时,它的第一个参数,应该是一个具有raw属性的对象,且raw属性的值应该是一个数组。
String.raw({ raw: 'test' }, 0, 1, 2);
// 't0e1s2t'
// 等同于
String.raw({ raw: ['t','e','s','t'] }, 0, 1, 2);
模板编译
模板自身封装方法
模板字符串可以通过 new Function 或 eval.call,将字符串组成的函数语句 转换成可执行的函数。
// 写法一
let str = 'return ' + "`Hello ${name}! I'm ${me}`";
let func = new Function('name', 'me', str);
func('Jack', 'Tome') // "Hello Jack!"
// 写法二
let str = '(name) => `Hello ${name}!`';
let func = eval.call(null, str);
func('Jack') // "Hello Jack!"
模板实例
现有一个数据
const data = ['1模板内容111','2模板内容222','3模板内容333']
如何封装一个方法以列表的形式将数据展示出来。那就需要封装一个模板。
1. 首先有一个原始模板
// 期望
<ul>
<li>1模板内容111</li>
<li>2模板内容222</li>
<li>3模板内容333</li>
</ul>
//原始模板
var template = `
<ul>
<% for(var i=0; i < data.length; i++) { %>
<li><%= data[i] %></li>
<% } %>
</ul>`;
2.使用ES5语法的循环拼接字符串,这里设置一个循环拼接函数为echo() 将其转换为JavaScript表达式字符串
echo('<ul>');
for(var i=0; i < data.length; i++) {
echo('<li>');
echo(data[i]);
echo('</li>');
};
echo('</ul>');
3. 用正则来匹配将基础字符串模板替换为想要的字符串模板
var evalExpr = /<%=(.+?)%>/g
var expr = /<%([\s\S]+?)%>/g
templateX = template
.replace(evalExpr, '`); \n echo( $1 ); \n echo(`')
.replace(expr, '`); \n $1 \n echo(`')
templateX = 'echo(`' + templateX + '`);'
4.将template封装成一个输出模板的函数内容的模板字符串。(通过 new Function 或 eval.call,将字符串组成的函数语句 转换成可执行的函数。)
var script =
`(function parse(data){
var output = "";
function echo(html){
output += html;
}
${ templateX }
return output;
})`
5. 完整的封装
function compile(template){
var evalExpr = /<%=(.+?)%>/g;
var expr = /<%([\s\S]+?)%>/g;
template = template
.replace(evalExpr, '`); \n echo( $1 ); \n echo(`')
.replace(expr, '`); \n $1 \n echo(`');
template = 'echo(`' + template + '`);';
var script =
`(function parse(data){
var output = "";
function echo(html){
output += html;
}
${ template }
return output;
})`;
return script;
}
var parse = eval(compile(template));
let innerHTML = parse(data);