下面是老的前 ES6 方式:
var name = "Kyle";
var greeting = "Hello " + name + "!";
console.log( greeting ); // "Hello Kyle!"
console.log( typeof greeting ); // "string"
下面是新的 ES6 方式:
var name = "Kyle";
var greeting = `Hello ${name}!`;
console.log( greeting ); // "Hello Kyle!"
console.log( typeof greeting ); // "string"
你可以看到,这里在一组字符外用 `..` 来包围,这会被解释为一个字符串字面量,但是其中任何 ${..} 形式的表达式都会被立即在线解析求值。这种形式的解析求值形式就是插入,你可以看到,这里在一组字符外用 `..` 来包围,这会被解释为一个字符串字面量,但是其中任何 ${..} 形式的表达式都会被立即在线解析求值。这种形式的解析求值形式就是插入
插入字符串字面量的一个优点是它们可以分散在多行:
var text =
`Now is the time for all good men
to come to the aid of their
country!`;
console.log( text );
// Now is the time for all good men
// to come to the aid of their
// country!
插入字符串字面量中的换行(新行)会在字符串值中被保留。
在字面量值中,除非作为明确的转义序列出现,\r 回车符(码点 U+000D)的值或者回车行符 \r\n(码点 U+000D 和 U+000A)都会被标准化为 \n 换行符(码点 U+000A)。但是别担心,这种标准化非常少见,很可能只有在复制粘贴文本到 JavaScript 文件的时候才会出现。
插入表达式
在插入字符串字面量的 ${..} 内可以出现任何合法的表达式,包括函数调用、在线函数表达式调用,甚至其他插入字符串字面量!
function upper(s) {
return s.toUpperCase();
}
var who = "reader";
var text =
`A very ${upper( "warm" )} welcome
to all of you ${upper( `${who}s` )}!`;
console.log( text );
// A very WARM welcome
// to all of you READERS!
这里,与 who + "s" 的形式相比,内层的 `${who}s` 插入字符串字面量对我们合并变量 who和字符串 "s" 来说会方便一点。嵌套插入字符串字面量在一些情况下是有所帮助的,但是如果你发现需要频繁使用这种形式,那么就要警惕了,不然你会发现自己得嵌套好多层。如果是这样的话,那么很可能你的字符串产生过程需要进行某种抽象。
标签模板字面量
举个例子:
function foo(strings, ...values) {
console.log( strings );
console.log( values );
}
var desc = "awesome";
foo`Everything is ${desc}!`;
// [ "Everything is ", "!"]
// [ "awesome" ]
首先跳出的最不和谐的部分是foo`Everything...`;。这种形式之前没有出现过。这是什么?本质上说,这是一类不需要 ( .. ) 的特殊函数调用。标签(tag)部分,即 `..` 字符串字面量之前的 foo 这一部分 , 是一个要调用的函数值。实际上,它可以是任意结果为函数的表达式,甚至可以是一个结果为另一个函数的函数调用,就像下面这样:
function bar() {
return function foo(strings, ...values) {
console.log( strings );
console.log( values );
}
}
var desc = "awesome";
bar()`Everything is ${desc}!`;
// [ "Everything is ", "!"]
// [ "awesome" ]
第一个参数,名为 strings,是一个由所有普通字符串(插入表达式之间的部分)组成的数组。得到的 strings 数组中有两个值:"Everything is" 和 "!"。
我们的例子中使用 ...gather/rest 运算符把其余所有参数值收集到名为 values 的数组中,这是为了方便起见,当然也可以把 strings 参数后面的其余部分都作为独立命名的参数。
你可以这样看待这两个数组:values 中的值是分隔符,就好像用它们连接在 strings 中的值,然后把所有这些都连接到一起,就得到了一个完成的插入字符串值。
标签字符串字面量就像是一个插入表达式求值之后,在最后的字符串值编译之前的处理步骤,这个步骤为从字面值产生字符串提供了更多的控制。
一般来说,字符串字面量标签函数(前面代码中的 foo(..))要计算出一个适当的字符串并将其返回,这样就可以像使用非标签字符串字面量一样把标签字符串字面量作为一个值来使用了:
function tag(strings, ...values) {
return strings.reduce( function(s,v,idx){
return s + (idx > 0 ? values[idx-1] : "") + v;
}, "" );
}
var desc = "awesome";
var text = tag`Everything is ${desc}!`;
console.log( text ); // Everything is awesome!
在这段代码中,tag(..) 是一个直通操作,因为它不执行任何具体修改,而只是使用reduce(..) 进行循环,把 strings 和 values 连接到一起,就像非标签字符串字面量所做的一样。
应用:用来把数字格式化为美元表示法(类似于简单的本地化)
function dollabillsyall(strings, ...values) {
return strings.reduce( function(s,v,idx){
if (idx > 0) {
if (typeof values[idx-1] == "number") {
// 看,这里也使用了插入字符串字面量!
s += `$${values[idx-1].toFixed( 2 )}`;
}
else {
s += values[idx-1];
}
}
return s + v;
}, "" );
}
var amt1 = 11.99,
amt2 = amt1 * 1.08,
name = "Kyle";
var text = dollabillsyall
`Thanks for your purchase, ${name}! Your
product cost was ${amt1}, which with tax
comes out to ${amt2}.`
console.log( text );
// Thanks for your purchase, Kyle! Your
// product cost was $11.99, which with tax
// comes out to $12.95.
如果在 values 中遇到一个 number 值,就在其之前放一个 "$",然后用 toFixed(2) 把它格式化为两个十进制数字的形式,否则就让这个值直接通过而不做任何修改。
原始(raw)字符串
在前面的代码中,标签函数接收第一个名为 strings 的参数,这是一个数组。但是还包括了一些额外的数据:所有字符串的原始未处理版本。可以像下面这样通过 .raw 属性访问这些原始字符串值:
function showraw(strings, ...values) {
console.log( strings );
console.log( strings.raw );
}
showraw`Hello\nWorld`;
// [ "Hello
// World" ]
// [ "Hello\nWorld" ]
原始版本的值保留了原始的转义码 \n 序列(\ 和 n 是独立的字符)
ES6 提供了一个内建函数可以用作字符串字面量标签:String.raw(..)。它就是传出strings 的原始版本:
console.log( `Hello\nWorld` );
// Hello
// World
console.log( String.raw`Hello\nWorld` );
// Hello\nWorld
String.raw`Hello\nWorld`.length;
// 12
字符串字面量标签的其他应用包括全球化、本地化等的特殊处理。