@lxjwlt
2015-11-08T19:00:27.000000Z
字数 3132
阅读 4087
博文
ES6提供了一种新的字符串格式化的语法功能:模板字符串(template string),该语法允许字符串内嵌代码表达式。本文简要介绍其中的使用。
注:下面临时写的几个字符串结合函数只起到说明的作用,容错率低,不可用于实际开发,开发中应使用长期维护的代码库,比如ESJ模板引擎、mustache、jade...
var user = {name: 'lxjwlt',age:23};user.name + ' who is in age ' + user.age + '.';// 结果:"lxjwlt who is in age 23."
项目开发中,这类代码很常见,但遇到更复杂的场景时,比如构建html,这类代码会变得难以书写,可读性差,而且维护难度大。
这时,我们可以使用字符串格式化函数来处理,简要例子:
format('{0} who is in age {1}', user.name, user.age);// 结果:"lxjwlt who is in age 23."function format (template) {var reg = /\{(\d)\}/g,dataList = Array.prototype.slice.call(arguments, 1);return template.replace(reg, function (match, $1) {return dataList[$1];});}
这是最简单的字符串格式化,但是这种方式下,字符串中无法嵌入表达式,也就是说控制字符串生成的逻辑代码必须写到JS代码中。
所以再进一步,再复杂一点,我们希望模板内可以使用表达式,下面我们来实现一个最简单的模板引擎,只实现表达式和条件语句:
var user = {name: 'lxjwlt',age: 23};template('%this.name% is a %if (this.age > 18) {% man %} else {% child %}%.', user);// 结果:"lxjwlt is a adult ."// 模板引擎function template (template, context) {var templateCode = templateToCode(template);return (new Function(templateCode)).apply(context);}// 模板转换为可执行代码function templateToCode (template, context) {var reg = /%([^%]+)%/g,conditionReg = /if \(.+\) \{|\} else \{|\}/,code = ['var str = "";'],lastIndex = 0,matchs;while (matchs = reg.exec(template)) {code.push('str = str + "' +template.slice(lastIndex, reg.lastIndex - matchs[0].length) +'";');if (conditionReg.test(matchs[1])) {code.push(matchs[1]);} else {code.push('str = str + (' + matchs[1] + ');');}lastIndex = reg.lastIndex;}code.push('str = str + "' + template.slice(lastIndex) + '";');code.push('return str;');return code.join('');}
模板引擎使得模板与代码分离开来,同时,模板中也保留了逻辑处理。javascript框架中通常都会带有模板引擎功能。
上面列举了字符串三种方式,从字符串相加,到字符串格式化处理函数,再到模板引擎,三种方式各不同,使用场景也不一样,对应着不同的需求。那么,ES6引入的模板字符串语法实现了什么功能?能代替上述三种方式么?
首先,我们来看看最基本使用:
var username = 'lxjwlt';`hi! ${username}.`; // "hi! lxjwlt."
注意!上面用的是反引号来包裹住字符串的,而不是单引号。反引号在键盘tab键的正上方。下面列出引号和反引号,我们可以仔细分辨一下其中的区别:
"" 双引号'' 单引号`` 反引号我们能发现,用反引号括住文本内容,文本内容中使用${}字面量插入逻辑表达式,这样就构成了一个模板字符串了。
ES6模板字符串的兼容性如下:
如果我们的chrome版本不太旧的话,现在完全可以打开控制台,我们马上来一发:
`${1+1}`; // "2"
有些情况下,我们需要对字符串中插入的值进行预处理,比如后台返回的值要进行htmlEncode处理,模板字符串支持使用函数来进行处理,该函数接收的参数形式如下:
strings, [value1, [value2, [value3 ...]]]
字符串会被${}分隔成一个个字符串片段保存在一个数组中,在函数的第一个参数中传入,对应上面的strings,而${}中表达式的返回值则从函数的其他参数中依次传入。我们现在可以实现“安全的”模板字符串了:
var username = '<span>click me!</span>'safeTemplate`hi! ${username}.`;// 结果:"hi! <span>click me!</span>"function safeTemplate (strings) {var str = [],values = Array.prototype.slice.call(arguments, 1),i;for (i = 0; i < strings.length; i++) {str.push(strings[i]);str.push(htmlEncode(values[i])); // 对插入值进行htmlEncode}return str.join('');}function htmlEncode () { /* ... */ }
实际开发中,我们的模板可能会以字符串的形式从别的地方请求回来,这时候要把字符串转换为模板字符串,然而js中没有提供任何转换的方法。下面我们自己来实现一个:
format("hi! ${name}", {name: "lxjwlt"}); // "hi! lxjwlt"function format (template, data) {var keys = Object.keys(data),dataList;dataList = keys.map(function (key) {return data[key]});// 这里使用反引号来构建模板引擎return new Function(keys.join(','), 'return `' + template + '`;').apply(null, dataList);}
有局限性
ES6模板字符串其实更准确的叫法是格式化字符串,它不是模板引擎,其中可插入的表达式不支持这种写法:
`${if (true)}`; // Uncaught SyntaxError: Unexpected token if`${true ? 1 :2}`; // Uncaught SyntaxError: Unexpected token ILLEGAL
所以ES6模板字符串不能代替模板引擎,只能在字符串结合逻辑不复杂的场景下使用。