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!' // => \\u000AES5中,当我们试着判断一个字符串片段是否包含在另一个字符串时,一般会考虑使用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,因为不包含最后索引的位置注意,所有的索引都是只包含开头索引的位置,不包含结束索引的位置
有时我们想将一个字符串复制累加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,则会报错。
JS内部,字符以UTF-16的格式存储,一般而言每个字符固定为2个字节。同时,JS允许使用\uxxxx形式来表示一个字符,其中xxxx表示码点。比如:
console.log('\u0061') //=> a console.log('\u0061'==='a');但是这种表示法只限于\u0000-\uFFFF之间的字符。超出这个范围的字符,必须用两个双字节形式表达。比如:‘��’字,它对应的码点表示是:\uD842\uFB7。
在ES6中,可以在\u后直接后面跟上超过0xFFF的数值,只要将码点至于大括号中:
\u{20BB7} \\=>��但是,这些需要字符实际上需要四个字节,于是会引起ES5中一些方法的错误。比如下面的例子:
var c = '��c' c.length // =>2 c.charAt(0) // =>'' c.charAt(1) // =>'' 因为字符是四字节而解析错误 c.charAt(2) // =>c c.charCodeAt(0) //=>55362 c.charCodeAt(1) //=> 57271ES6提供了对应的方法来解决这个问题:
// 使用codePointAt取代charCodeAt c.codePointAt(0) // => 134071 返回整个字符的码点,默认是十进制的形式,如果想转为16禁止,可以使用toString(16) c.codePointAt(1) // => 57271 返回后一半字符的码点 c.codePointAt(2) // 97 返回a的码点,但要注意,方法的参数是2 // 使用at取代charAt c.at(0) // =>'��' 在ES7中定义的方法