aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/web/javascript/reference/template_strings/index.html
blob: aec3adfb5b43520d0e82945deca4917a94241d52 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
---
title: 模板字符串
slug: Web/JavaScript/Reference/template_strings
tags:
  - ECMAScript6
  - JavaScript
  - Template string
  - 模板字符串
translation_of: Web/JavaScript/Reference/Template_literals
---
<div>{{JsSidebar("More")}} </div>

<p>模板字面量 是允许嵌入表达式的字符串字面量。你可以使用多行字符串和字符串插值功能。它们在ES2015规范的先前版本中被称为“模板字符串”。</p>

<h2 id="语法" style="font-size: 2.14285714285714rem;">语法</h2>

<pre class="syntaxbox">`string text`

`string text line 1
 string text line 2`

`string text ${expression} string text`

tag `string text ${expression} string text`
</pre>

<h2 id="描述" style="font-size: 2.14285714285714rem;">描述</h2>

<p>模板字符串使用反引号 (` `) 来代替普通字符串中的用双引号和单引号。模板字符串可以包含特定语法(<code style="font-style: normal;">${expression}</code>)的占位符。占位符中的表达式和周围的文本会一起传递给一个默认函数,该函数负责将所有的部分连接起来,如果一个模板字符串由表达式开头,则该字符串被称为带标签的模板字符串,该表达式通常是一个函数,它会在模板字符串处理后被调用,在输出最终结果前,你都可以通过该函数来对模板字符串进行操作处理。在模版字符串内使用反引号(`)时,需要在它前面加转义符(\)。</p>

<pre class="brush: js">`\`` === "`" // --&gt; true</pre>

<h3 id="多行字符串">多行字符串</h3>

<p>在新行中插入的任何字符都是模板字符串中的一部分,使用普通字符串,你可以通过以下的方式获得多行字符串:</p>

<pre class="brush: js">console.log('string text line 1\n' +
'string text line 2');
// "string text line 1
// string text line 2"</pre>

<p>要获得同样效果的多行字符串,只需使用如下代码:</p>

<pre class="brush: js">console.log(`string text line 1
string text line 2`);
// "string text line 1
// string text line 2"</pre>

<h3 id="插入表达式">插入表达式</h3>

<p>在普通字符串中嵌入表达式,必须使用如下语法:</p>

<p> </p>

<pre class="brush: js">var a = 5;
var b = 10;
console.log('Fifteen is ' + (a + b) + ' and\nnot ' + (2 * a + b) + '.');
// "Fifteen is 15 and
// not 20."</pre>

<p> </p>

<p>现在通过模板字符串,我们可以使用一种更优雅的方式来表示:</p>

<p> </p>

<pre class="brush: js">var a = 5;
var b = 10;
console.log(`Fifteen is ${a + b} and
not ${2 * a + b}.`);
// "Fifteen is 15 and
// not 20."</pre>

<p> </p>

<h3 id="嵌套模板">嵌套模板</h3>

<p>在某些时候,嵌套模板是具有可配置字符串的最简单也是更可读的方法。 在模板中,只需在模板内的占位符 <code>${ }</code> 内使用它们,就可以轻松地使用内部反引号。 例如,如果条件 a 是真的,那么返回这个模板化的文字。</p>

<p>ES5:</p>

<pre class="brush: js">var classes = 'header'
classes += (isLargeScreen() ?
   '' : item.isCollapsed ?
     ' icon-expander' : ' icon-collapser');</pre>

<p>在ES2015中使用模板文字而没有嵌套:</p>

<pre class="brush: js">const classes = `header ${ isLargeScreen() ? '' :
    (item.isCollapsed ? 'icon-expander' : 'icon-collapser') }`;
</pre>

<p> </p>

<p>在ES2015的嵌套模板字面量中:</p>

<p> </p>

<pre class="brush: js">const classes = `header ${ isLargeScreen() ? '' :
 `icon-${item.isCollapsed ? 'expander' : 'collapser'}` }`;</pre>

<p> </p>

<p> </p>

<h3 id="带标签的模板字符串">带标签的模板字符串</h3>

<p>更高级的形式的模板字符串是带标签的模板字符串。标签使您可以用函数解析模板字符串。标签函数的第一个参数包含一个字符串值的数组。其余的参数与表达式相关。最后,你的函数可以返回处理好的的字符串(或者它可以返回完全不同的东西 , 如下个例子所述)。用于该标签的函数的名称可以被命名为任何名字。</p>

<pre class="brush: js">var person = 'Mike';
var age = 28;

function myTag(strings, personExp, ageExp) {

  var str0 = strings[0]; // "that "
  var str1 = strings[1]; // " is a "

  // There is technically a string after
  // the final expression (in our example),
  // but it is empty (""), so disregard.
  // var str2 = strings[2];

  var ageStr;
  if (ageExp &gt; 99){
    ageStr = 'centenarian';
  } else {
    ageStr = 'youngster';
  }

  return str0 + personExp + str1 + ageStr;

}

var output = myTag`that ${ person } is a ${ age }`;

console.log(output);
// that Mike is a youngster</pre>

<p class="brush: js">正如下面例子所展示的,标签函数并不一定需要返回一个字符串。</p>

<pre class="brush: js">function template(strings, ...keys) {
  return (function(...values) {
    var dict = values[values.length - 1] || {};
    var result = [strings[0]];
    keys.forEach(function(key, i) {
      var value = Number.isInteger(key) ? values[key] : dict[key];
      result.push(value, strings[i + 1]);
    });
    return result.join('');
  });
}

var t1Closure = template`${0}${1}${0}!`;
t1Closure('Y', 'A');  // "YAY!"
var t2Closure = template`${0} ${'foo'}!`;
t2Closure('Hello', {foo: 'World'});  // "Hello World!"</pre>

<h3 id="原始字符串">原始字符串</h3>

<p>在标签函数的第一个参数中,存在一个特殊的属性<code>raw</code> ,我们可以通过它来访问模板字符串的原始字符串,而不经过特殊字符的替换。</p>

<pre class="brush: js">function tag(strings) {
  console.log(strings.raw[0]);
}

tag`string text line 1 \n string text line 2`;
// logs "string text line 1 \n string text line 2" ,
// including the two characters '\' and 'n'</pre>

<p>另外,使用{{jsxref("String.raw()")}} 方法创建原始字符串和使用默认模板函数和字符串连接创建是一样的。</p>

<pre class="brush: js line-numbers  language-js"><code class="language-js"><span class="keyword token">var</span> str <span class="operator token">=</span> String<span class="punctuation token">.</span>raw<span class="template-string token"><span class="string token">`Hi\n</span><span class="interpolation token"><span class="interpolation-punctuation punctuation token">${</span><span class="number token">2</span><span class="operator token">+</span><span class="number token">3</span><span class="interpolation-punctuation punctuation token">}</span></span><span class="string token">!`</span></span><span class="punctuation token">;</span>
<span class="comment token">// "Hi\n5!"</span>

str<span class="punctuation token">.</span>length<span class="punctuation token">;</span>
<span class="comment token">// 6</span>

str<span class="punctuation token">.</span><span class="function token">split</span><span class="punctuation token">(</span><span class="string token">''</span><span class="punctuation token">)</span><span class="punctuation token">.</span><span class="function token">join</span><span class="punctuation token">(</span><span class="string token">','</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="comment token">// "H,i,\,n,5,!"</span></code>
</pre>

<h3 id="带标签的模版字面量及转义序列">带标签的模版字面量及转义序列</h3>

<p>自ES2016起,带标签的模版字面量遵守以下转义序列的规则:</p>

<ul>
 <li>Unicode字符以"\u"开头,例如<code>\u00A9</code></li>
 <li>Unicode码位用"\u{}"表示,例如<code>\u{2F804}</code></li>
 <li>十六进制以"\x"开头,例如<code>\xA9</code></li>
 <li>八进制以"\"和数字开头,例如<code>\251</code></li>
</ul>

<p>这表示类似下面这种带标签的模版是有问题的,因为对于每一个ECMAScript语法,解析器都会去查找有效的转义序列,但是只能得到这是一个形式错误的语法:</p>

<pre class="brush: js">latex`\unicode`
// 在较老的ECMAScript版本中报错(ES2016及更早)
// SyntaxError: malformed Unicode character escape sequence
</pre>

<h4 id="ES2018关于非法转义序列的修订">ES2018关于非法转义序列的修订</h4>

<p>带标签的模版字符串应该允许嵌套支持常见转义序列的语言(例如<a href="https://en.wikipedia.org/wiki/Domain-specific_language">DSLs</a><a href="https://en.wikipedia.org/wiki/LaTeX">LaTeX</a>)。ECMAScript提议<a href="https://tc39.github.io/proposal-template-literal-revision/">模版字面量修订</a>(第4阶段,将要集成到ECMAScript 2018标准) 移除对ECMAScript在带标签的模版字符串中转义序列的语法限制。</p>

<p>不过,非法转义序列在"cooked"当中仍然会体现出来。它们将以 {{jsxref("undefined")}} 元素的形式存在于"cooked"之中:</p>

<pre class="brush: js">function latex(str) {
 return { "cooked": str[0], "raw": str.raw[0] }
}

latex`\unicode`

// { cooked: undefined, raw: "\\unicode" }</pre>

<p>值得注意的是,这一转义序列限制只对带标签的模板字面量移除,而不包括不带标签的模板字面量:</p>

<pre class="brush: js example-bad">let bad = `bad escape sequence: \unicode`;</pre>

<h2 id="规范" style="font-size: 2.14285714285714rem;">规范</h2>

<table class="standard-table">
 <tbody>
  <tr>
   <th scope="col">Specification</th>
   <th scope="col">Status</th>
   <th scope="col">Comment</th>
  </tr>
  <tr>
   <td>{{SpecName('ES2015', '#sec-template-literals', 'Template Literals')}}</td>
   <td>{{Spec2('ES2015')}}</td>
   <td>Initial definition. Defined in several section of the specification: <a href="http://www.ecma-international.org/ecma-262/6.0/#sec-template-literals">Template Literals</a>, <a href="http://www.ecma-international.org/ecma-262/6.0/#sec-tagged-templates">Tagged Templates</a></td>
  </tr>
  <tr>
   <td>{{SpecName('ESDraft', '#sec-template-literals', 'Template Literals')}}</td>
   <td>{{Spec2('ESDraft')}}</td>
   <td>Defined in several section of the specification: <a href="https://tc39.github.io/ecma262/#sec-template-literals">Template Literals</a>, <a href="https://tc39.github.io/ecma262/#sec-tagged-templates">Tagged Templates</a></td>
  </tr>
  <tr>
   <td><a href="https://tc39.github.io/proposal-template-literal-revision/">Template Literal Revision</a></td>
   <td>Stage 4 draft</td>
   <td>Drops escape sequence restriction from tagged templates</td>
  </tr>
 </tbody>
</table>

<h2 id="浏览器兼容" style="font-size: 2.14285714285714rem;">浏览器兼容</h2>

<div>


<p>{{Compat("javascript.grammar.template_literals")}}</p>
</div>

<h2 id="相关链接" style="font-size: 2.14285714285714rem;">相关链接</h2>

<ul>
 <li>{{jsxref("String")}}</li>
 <li>{{jsxref("String.raw()")}}</li>
 <li><a href="/en-US/docs/Web/JavaScript/Reference/Lexical_grammar">Lexical grammar</a></li>
 <li><a href="https://gist.github.com/WebReflection/8f227532143e63649804">Template-like strings in ES3 compatible syntax</a></li>
 <li><a href="https://hacks.mozilla.org/2015/05/es6-in-depth-template-strings-2/">"ES6 in Depth: Template strings" on hacks.mozilla.org</a></li>
</ul>