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
|
---
title: 樣板字面值
slug: Web/JavaScript/Reference/Template_literals
tags:
- ECMAScript 2015
- Guide
- JavaScript
- String
- Template Strings
- Template literals
- Template string
- strings
translation_of: Web/JavaScript/Reference/Template_literals
---
<div>{{JsSidebar("More")}}</div>
<p>樣板字面值(Template literals)是允許嵌入運算式的字串字面值(string literals)。你可以透過樣板字面值來使用多行字串及字串內插(string interpolation)功能。他們在 ES2015 規範的先行版本中被稱為「樣板字串(template strings)」。</p>
<h2 id="語法">語法</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="描述">描述</h2>
<p>樣板字面值(Template literals)被反引號(back-tick,<a href="https://zh.wikipedia.org/wiki/%E9%87%8D%E9%9F%B3%E7%AC%A6">重音符號</a>):` ` 字元封閉,代替了雙或單引號。樣板字面值可以包含由錢字元及花括號所構成(<code>${expression}</code>)的佔位符(placeholders)。這個在佔位符中的運算式以及在它們之間的文字會被傳入一個函式。預設函式只是將這些部分組合成一個單一的字串。如果在樣板字面值前有一個運算式(<code>tag</code> here),則此樣板字串被稱為「標籤樣板字面值(tagged template literal)」。在此情況下,標籤運算式(通常是一個函式)會被呼叫來處理樣板字面值,讓你可以在函式回傳之前進行操作。要在樣板字面值中跳脫一個反引號,可以於反引號前加上一個反斜線(backslash)<strong>\ </strong>。</p>
<pre class="brush: js">`\`` === '`' // --> 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>
<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>
<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>
<h3 id="巢狀樣板">巢狀樣板</h3>
<p>In certain times, nesting a template is the easiest and perhaps more readable way to have configurable strings. Within a backticked template it is simple to allow inner backticks simply by using them inside a placeholder <code>${ }</code> within the template. For instance, if condition a is true: then return this templated literal.</p>
<p>In ES5:</p>
<pre class="brush: js">var classes = 'header'
classes += (isLargeScreen() ?
'' : item.isCollapsed ?
' icon-expander' : ' icon-collapser');
</pre>
<p>In ES2015 with template literals and without nesting:</p>
<pre class="brush: js">const classes = `header ${ isLargeScreen() ? '' :
(item.isCollapsed ? 'icon-expander' : 'icon-collapser') }`;</pre>
<p>In ES2015 with nested template literals:</p>
<pre class="brush: js">const classes = `header $<strong>{</strong> isLargeScreen() ? '' :
`icon-${item.isCollapsed ? 'expander' : 'collapser'}`<strong> </strong><strong>}`</strong>;</pre>
<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 > 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>標籤函數不一定要回傳一個字串,如下列範例:</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> 」,允許你獲取原始輸入的、未處理任何<a href="/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Using_special_characters_in_strings">轉義序列</a>的字串值。</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">var str = String.raw`Hi\n${2+3}!`;
// "Hi\n5!"
str.length;
// 6
str.split('').join(',');
// "H,i,\,n,5,!"
</pre>
<h3 id="標籤樣板字面值和跳脫序列">標籤樣板字面值和跳脫序列</h3>
<p>在 ES2016 的規範中,標籤樣板字面值遵守下列跳脫序列(escape sequences)規則:</p>
<ul>
<li>萬國碼 (Unicode) 跳脫序列由 "\u" 作為開頭, 例: <code>\u00A9</code></li>
<li>萬國碼位 (Unicode code point) 由 "\u{}" 作為開頭, 例: <code>\u{2F804}</code></li>
<li>十六進位制碼由 "\x" 作為開頭, 例: <code>\xA9</code></li>
<li>十進位制碼由 "\" 作為開頭, 例: \251</li>
</ul>
<p>這表示像是下述的標籤樣板字面值是有問題的,因為根據 ECMAScript 規範,一個語法分析器會嘗試以萬國碼轉義序列去解析它,然後發現序列有誤:</p>
<pre class="brush: js">latex`\unicode`
// Throws in older ECMAScript versions (ES2016 and earlier)
// SyntaxError: malformed Unicode character escape sequence</pre>
<p>Tagged template literals should allow the embedding of languages (for example <a href="https://en.wikipedia.org/wiki/Domain-specific_language">DSLs</a>, or <a href="https://en.wikipedia.org/wiki/LaTeX">LaTeX</a>), where other escapes sequences are common. The ECMAScript proposal <a href="https://tc39.github.io/proposal-template-literal-revision/">Template Literal Revision</a> (stage 4, to be integrated in the ECMAScript 2018 standard) removes the syntax restriction of ECMAScript escape sequences from tagged template literals.</p>
<p>However, illegal escape sequence must still be represented in the "cooked" representation. They will show up as {{jsxref("undefined")}} element in the "cooked" array:</p>
<p>l be represented in the "cooked" representation. They will show up as {{jsxref("undefined")}} element in the "cooked" array:</p>
<p> </p>
<pre class="brush: js">function latex(str) {
return { "cooked": str[0], "raw": str.raw[0] }
}
latex`\unicode`
// { cooked: undefined, raw: "\\unicode" }</pre>
<p>Note that the escape sequence restriction is only dropped from <em>tagged</em> template literals and not from <em>untagged</em> template literals:</p>
<pre class="brush: js example-bad">let bad = `bad escape sequence: \unicode`;</pre>
<h2 id="規範">規範</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 template literals</td>
</tr>
</tbody>
</table>
<h2 id="瀏覽器相容性">瀏覽器相容性</h2>
<div>
<p>{{Compat("javascript.grammar.template_literals")}}</p>
</div>
<h2 id="參見">參見</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>
|