4.1模板字符串与模板标签ES6加强了对Unicode的支持,并且扩展了字符串对象。本章把重点放在ES6对对象方法的括展上,至于字符Unicode表示法的加强,则不打算详细展开。本章主要包括:
- 模板字符串与标签模板
- 字符串括展方法
- 字符的Unicode表示法(简介)
模板字符串
在第一章我们已经大体介绍了模板字符串的使用方法,先让我们回顾一下。
使用模板字符串可以轻松的在字符串中进行变量的替换。注意使用模板时,最外层为``号(反引号),变量使用${}包裹。${}的花括号中可以是任意表达式。如果表达式的返回值不是字符串,则将按照一般规则转化为字符串。比如如果大括号中是一个对象的话,将默认调用对象的toString方法。
let name = 'Kyle';
let foo = `学生${name}正在学习ES6`;
再比如:
let student = {
name : 'Kyle',
toString: function () {
return 'Agent' + this.name
}
};
let foo = `学生${student}正在学习ES6`;
console.log(foo); // 输出:学生AgentKyle正在学习ES6
这解决了ES5中换行和引入变量琐碎不便的问题。下面是一个使用模板字符串拼接和普通字符串拼接DOM并插入到页面的例子,你可以明显的发现前者要方便很多:
let arr = ['first', 'second', 'third'];
let ul = `<ul>
<li>${arr[0]}</li>
<li>${arr[1]}</li>
<li>${arr[2]}</li>
</ul>`;
let normalUl = '<ul>\
<li>'+arr[0]+'</li>\
<li>'+arr[1]+'</li>\
<li>'+arr[2]+'</li>\
</ul>';
document.getElementById('container').innerHTML = ul;
document.getElementById('normal-container').innerHTML = normalUl;
上面的代码使用的两种方式呈现的最终效果是一致的,但现在我们稍作修改,把下面的两个innerHTML改为innerText,运行结果:
如果使用模板字符串表示多行字符串,所有的空格和缩进会被保留在输出中。查看源码可以发现,浏览器在处理时使用了br标签。
标签模板
标签模板其实并非模板,而是一种函数的调用方式。标签其实就是一个函数,其格式是:
标签函数`模板字符串`
其中标签函数会被默认注入若干个参数,第一个参数是一个数组,其中的元素是模板字符串非变量与变量之间的各个部分,随后的参数是模板字符串中的变量被替换后的值。显然,如果模板字符串中有n个变量,标签函数就会有n+1个参数。这可能听起来有些抽象,我们看下面的例子就一目了然了:
let student = {
id:'2014556698',
name:'AgentKyle'
};
function tagFun(arr, ...args) {
arr.forEach((item) => {
console.log(item);
});
args.forEach((item) => {
console.log(item);
})
return 'ok!'
}
tagFun`学生姓名:${student.name},学生id:${student.id};`; // =>ok!
运行结果,控制台输出:
学生姓名:
,学生id:
;
2014556698
AgentKyle
需要注意标签函数第一个参数数组的最后一个元素,它始终表示最后一个变量到模板字符串末尾的部分,在上面的例子中,这部分有一个;(分号)
。但如果这部分什么也没有,模板就结束了,则其最后一个元素将会是''(空)
。模板标签表达式的最终返回值即标签函数的返回值。
下面的标签函数可以返回像不使用模板标签那样的填充模板:
function tagFun() {
let str = '',
strArr = arguments[0];
for(let i = 0; i<strArr.length; i++){
str+=strArr[i];
str+=arguments[i+1]||''
}
return str;
}
引入标签模板的一个目的在于对模板字符串本身进行更灵活的控制,例如去除容易引起安全问题的危险字符,对模板字符进行国际化等等。
此外,你可以使用模板标签在JS语言中嵌入其他语言:
jsx`
<input ref="input" onchange=${this.handleChange} >
`
上面通过jsx函数将一个DOM字符串转成了React对象。你可以在github上找到jsx函数的源码。
此外,标签函数的第一个参数还具有一个row属性,它是一个和其宿主数组几乎一模一样的数组,唯一的区别在于其中的字符串元素的反斜线都被转译(前面加了一个\)了。String构造器也原生提供了String.row静态方法,用来生成一个反斜线都被转译的字符串:
String.row`hello world\n${1+1}` // => hello world\\n2
String.row'\u000A!' // => \\u000A
4.2 字符串的扩展方法
includes,startsWith,endsWith
ES5中,当我们试着判断一个字符串片段是否包含在另一个字符串时,一般会考虑使用IndexOf的方式:
"But those who wait for the Lord will renew their strength".indexOf('shall') // =>-1
当其匹配成功时,会返回匹配到的位置。若是不存在,则会返回-1。这固然很好,但问题是有的时候我们只不过是想知道A串是否包含在B串内,并不想知道它在哪里。而且匹配失败时返回-1的特性无形中增加了编码量以及出错的概率:
var flag = "But those who wait for the Lord will renew their strength".indexOf('shall')
if(flag === -1){}
if(flag){} // 容易出错的逻辑,这样其实是指的从字符串首匹配成功就不执行里面的代码片段
ES6中提供了三中新方法:
- includes() :返回布尔值,表示是否找到了字符串
- startsWith():返回布尔值,表示参数字符串是否在源字符串的开头
- endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部
它们都可以接受第二个参数以控制查找的范围,但endsWith与其它的方法行为不同,其它两个方法都是从规定的索引一直到字符串最后进行匹配,而endsWith则是从字符串的开头到规定的索引的中间段进行查找。下面是一些例子:
let str = "But those who wait for the Lord will renew their strength";
console.log(str.includes('will')); //t
console.log(str.includes('will',40)); //f
console.log(str.startsWith('But'));//t
console.log(str.startsWith('but')); //f 匹配区分大小写
console.log(str.startsWith('those',4)); // t 注意这里是4,而并非5,因为包含开头索引的位置
console.log(str.endsWith('those')); // f
console.log(str.endsWith('those',9)); // t 注意这里是9,而不是8,因为不包含最后索引的位置
注意,所有的索引都是只包含开头索引的位置,不包含结束索引的位置
repeat
有时我们想将一个字符串复制累加n次:
let str = 'hello';
let repeatedStr = function (n) {
let newStr = '';
for (let i = 0; n < 5; i++) {
newStr+=str;
}
return newStr;
}(5);
console.log(repeatedStr);
ES6提供了原生的解决方案:
let repeatedStr = str.repeat(5);
它接收一个数值型参数,如果传入一个小数,则会向下取整;0到-1之间的小数,取整后等价于-0,即0;如果传入一个字符串类型的数,则会先转换成数值类型再进行计算。参数NaN等价于0。
如果其参数是非0到-1之间负数或Infinity,则会报错。
4.3 字符的Unicode表示法详见阮一峰大神的ES6教程