\n
)String.raw
ES6 有兩種新的字串:範本字串和標記範本字串。這兩種字串名稱相似,外觀也相似,但它們卻截然不同。因此,區分它們非常重要
範本字串是字串字面值,可以跨多行,並包含內插的運算式(透過 ${···}
插入)
const
firstName
=
'Jane'
;
console
.
log
(
`Hello
${
firstName
}
!
How are you
today?`
);
// Output:
// Hello Jane!
// How are you
// today?
標記範本字串(簡稱:標記範本)是透過在範本字串之前提及函式而建立的
> String.raw`A \tagged\ template`
'A \\tagged\\ template'
標記範本是函式呼叫。在先前的範例中,會呼叫方法 String.raw
以產生標記範本的結果。
字面值是產生值的語法結構。範例包括字串字面值(產生字串)和正規表示式字面值(產生正規表示式物件)。ECMAScript 6 有兩個新的字面值
請務必記住,範本字串和標記範本的名稱有點誤導。它們與網頁開發中經常使用的範本無關:可透過(例如)JSON 資料填入空白的文字檔。
範本字串是一種新的字串字面值,可以跨多行,並內插運算式(包含其結果)。例如
const
firstName
=
'Jane'
;
console
.
log
(
`Hello
${
firstName
}
!
How are you
today?`
);
// Output:
// Hello Jane!
// How are you
// today?
字面值本身以反引號(`
)為界,字面值內的內插運算式以 ${
和 }
為界。範本字串總是產生字串。
反斜線用於範本字串中的跳脫。
它讓您可以在範本字串中提及反引號和 ${
> `\``
'`'
> `$` // OK
'$'
> `${
`
SyntaxError
>
`\${`
'${'
>
`
\$
{
}
`
'${}'
除此之外,反斜線在字串字面值中的作用與在範本字串中的作用相同
> `\\`
'\\'
> `\n`
'\n'
> `\u{58}`
'X'
\n
) 終止行的常見方式為
\n
, U+000A):Unix 使用(包含目前的 macOS)\r
, U+000D):舊版 Mac OS 使用。\r\n
):Windows 使用。所有這些換行符號在範本文字中都標準化為 LF。也就是說,以下程式碼在所有平台上都會記錄 true
const
str
=
`BEFORE
AFTER`
;
console
.
log
(
str
===
'BEFORE\nAFTER'
);
// true
以下是標籤範本文字(簡稱:標籤範本)
tagFunction
`Hello
${
firstName
}
${
lastName
}
!`
將範本文字放在表達式之後會觸發函式呼叫,類似於參數清單(括號中的逗號分隔值)如何觸發函式呼叫。前一個程式碼等於以下函式呼叫(實際上,第一個參數不只是一個陣列,但這會在稍後說明)。
tagFunction
([
'Hello '
,
' '
,
'!'
],
firstName
,
lastName
)
因此,反引號中內容之前的名稱是要呼叫的函式名稱,也就是標籤函式。標籤函式會收到兩種不同類型的資料
'Hello '
。firstName
(以 ${}
分隔)。替換可以是任何表達式。範本字串在靜態時(編譯時)已知,替換只在執行時已知。標籤函式可以隨意處理其參數:它可以完全忽略範本字串、傳回任何類型的值等。
此外,標籤函式會取得每個範本字串的兩個版本
`\n`
變成 '\\n'
,長度為 2 的字串)`\n`
變成只包含換行符號的字串)。這允許 String.raw
(稍後會說明)發揮作用
> String.raw`\n` === '\\n'
true
標記範本字串可讓您輕鬆實作自訂內嵌子語言(有時稱為特定領域語言),因為 JavaScript 會為您處理大部分的剖析。您只需撰寫一個接收結果的函式即可。
讓我們來看幾個範例。其中一些範例的靈感來自 範本字串的原始提案,該提案以舊名稱類字串來稱呼範本字串。
ES6 包含標籤函式 String.raw
,用於原始字串,其中反斜線沒有特殊意義
const
str
=
String
.
raw
`This is a text
with multiple lines.
Escapes are not interpreted,
\
n is not a newline.`
;
當您需要建立包含反斜線的字串時,這會很有用。例如
function
createNumberRegExp
(
english
)
{
const
PERIOD
=
english
?
String
.
raw
`
\
.`
:
','
;
// (A)
return
new
RegExp
(
`[0-9]+(
${
PERIOD
}
[0-9]+)?`
);
}
在 A 行中,String.raw
讓我們可以像在正規表示式字串中一樣撰寫反斜線。使用一般字串字串時,我們必須跳脫兩次:首先,我們需要跳脫正規表示式的句點。其次,我們需要跳脫字串字串的反斜線。
const
proc
=
sh
`ps ax | grep
${
pid
}
`
;
(來源:David Herman)
const
buffer
=
bytes
`455336465457210a`
;
(來源:David Herman)
POST
`http://foo.org/bar?a=
${
a
}
&b=
${
b
}
Content-Type: application/json
X-Credentials:
${
credentials
}
{ "foo":
${
foo
}
,
"bar":
${
bar
}
}
`
(
myOnReadyStateChangeHandler
);
(來源:Luke Hoban)
Steven Levithan 已提供 一個範例,說明如何將標記範本字串用於他的正規表示式函式庫 XRegExp。
在沒有標記範本的情況下,您可以撰寫以下類型的程式碼
var
parts
=
'/2015/10/Page.html'
.
match
(
XRegExp
(
'^ # match at start of string only \n'
+
'/ (?<year> [^/]+ ) # capture top dir name as year \n'
+
'/ (?<month> [^/]+ ) # capture subdir name as month \n'
+
'/ (?<title> [^/]+ ) # capture base name as title \n'
+
'\\.html? $ # .htm or .html file ext at end of path '
,
'x'
));
console
.
log
(
parts
.
year
);
// 2015
我們可以看到 XRegExp 提供了命名群組(year
、month
、title
)和 x
旗標。使用該旗標,大多數空白會被忽略,而且可以插入註解。
有兩個原因導致字串字串在此處無法正常運作。首先,我們必須輸入每個正規表示式反斜線兩次,才能跳脫字串字串。其次,輸入多行很麻煩。
您也可以在下一行繼續字串字串,而不是新增字串,只要您使用反斜線結束當前行即可。但這仍然會造成許多視覺上的混亂,特別是因為您仍然需要在每行的結尾使用明確的新行字元 \n
。
var
parts
=
'/2015/10/Page.html'
.
match
(
XRegExp
(
'^ # match at start of string only \n\
/ (?<year> [^/]+ ) # capture top dir name as year \n\
/ (?<month> [^/]+ ) # capture subdir name as month \n\
/ (?<title> [^/]+ ) # capture base name as title \n\
\\.html? $ # .htm or .html file ext at end of path '
,
'x'
));
使用標記範本可以解決反斜線和多行的問題
var
parts
=
'/2015/10/Page.html'
.
match
(
XRegExp
.
rx
`
^ # match at start of string only
/ (?<year> [^/]+ ) # capture top dir name as year
/ (?<month> [^/]+ ) # capture subdir name as month
/ (?<title> [^/]+ ) # capture base name as title
\
.html? $ # .htm or .html file ext at end of path
`
);
此外,標記範本讓您可以透過 ${v}
插入值 v
。我希望正規表示式函式庫可以跳脫字串,並逐字插入正規表示式。例如
var
str
=
'really?'
;
var
regex
=
XRegExp
.
rx
`(
${
str
}
)*`
;
這等同於
var
regex
=
XRegExp
.
rx
`(really
\
?)*`
;
範例
$
`a.
${
className
}
[href*='//
${
domain
}
/']`
這是一個 DOM 查詢,用於尋找所有 CSS 類別為 className
且目標為具有給定網域的 URL 的 <a>
標籤。標籤函數 $
可確保參數正確轉譯,讓這種方法比手動字串串接更安全。
Facebook React 是「用於建構使用者介面的 JavaScript 函式庫」。它有可選的語言擴充功能 JSX,讓您能夠為使用者介面建構虛擬 DOM 樹。這個擴充功能讓您的程式碼更簡潔,但它也不是標準,且會中斷與其他 JavaScript 生態系統的相容性。
函式庫 t7.js 提供了 JSX 的替代方案,並使用標籤為 t7
的範本
t7
.
module
(
function
(
t7
)
{
function
MyWidget
(
props
)
{
return
t7
`
<div>
<span>I'm a widget
${
props
.
welcome
}
</span>
</div>
`
;
}
t7
.
assign
(
'Widget'
,
MyWidget
);
t7
`
<div>
<header>
<Widget welcome="Hello world" />
</header>
</div>
`
;
});
在「為何不使用範本字面值?」中,React 團隊解釋了他們為何選擇不使用範本字面值。其中一個挑戰是存取標籤範本內的元件。例如,MyWidget
是從前一個範例中的第二個標籤範本存取的。一種冗長的方法是
<
$
{
MyWidget
}
welcome
=
"Hello world"
/>
相反地,t7.js 使用透過 t7.assign()
填入的註冊表。這需要額外的設定,但範本字面值看起來更漂亮;特別是如果同時有開啟標籤和關閉標籤。
Facebook Relay 是「用於建構資料驅動 React 應用程式的 JavaScript 框架」。它的其中一部分是查詢語言 GraphQL,其查詢可以透過標籤為 Relay.QL
的範本建立。例如 (取自 Relay 首頁)
class
Tea
extends
React
.
Component
{
render
()
{
var
{
name
,
steepingTime
}
=
this
.
props
.
tea
;
return
(
<
li
key
=
{
name
}
>
{
name
}
(
<
em
>
{
steepingTime
}
min
<
/em>)
<
/li>
);
}
}
Tea
=
Relay
.
createContainer
(
Tea
,
{
fragments
:
{
// (A)
tea
:
()
=>
Relay
.
QL
`
fragment on Tea {
name,
steepingTime,
}
`
,
},
});
class
TeaStore
extends
React
.
Component
{
render
()
{
return
<
ul
>
{
this
.
props
.
store
.
teas
.
map
(
tea
=>
<
Tea
tea
=
{
tea
}
/>
)}
<
/ul>;
}
}
TeaStore
=
Relay
.
createContainer
(
TeaStore
,
{
fragments
:
{
// (B)
store
:
()
=>
Relay
.
QL
`
fragment on Store {
teas {
${
Tea
.
getFragment
(
'tea'
)
}
},
}
`
,
},
});
從 A 行和 B 行開始的物件定義了片段,這些片段是透過傳回查詢的回呼函式定義的。片段 tea
的結果會放入 this.props.tea
。片段 store
的結果會放入 this.props.store
。
這是查詢運作的資料
const
STORE
=
{
teas
:
[
{
name
:
'Earl Grey Blue Star'
,
steepingTime
:
5
},
···
],
};
這個資料會包裝在 GraphQLSchema
的執行個體中,並在那裡取得名稱 Store
(如 fragment on Store
中所述)。
本節說明一種簡單的文字在地化方法,支援不同的語言和不同的地區設定 (數字、時間等格式)。假設有以下訊息。
alert
(
msg
`Welcome to
${
siteName
}
, you are visitor
number
${
visitorNumber
}
:d!`
);
標籤函式 msg
會執行下列動作。
首先,將文字部分串接成一個字串,用於在表格中查詢翻譯。前一個範例的查詢字串為
'Welcome to {0}, you are visitor number {1}!'
例如,這個查詢字串可以對應到德語翻譯:
'Besucher Nr. {1}, willkommen bei {0}!'
英文「翻譯」會和查詢字串相同。
其次,使用查詢結果來顯示替換。由於查詢結果包含索引,因此可以重新排列替換順序。這已在德語中完成,其中訪客編號在網站名稱之前。替換格式可透過註解 (例如 :d
) 來設定。此註解表示應對 visitorNumber
使用特定地區設定的小數點分隔符號。因此,可能的英文結果為
Welcome to ACME Corp., you are visitor number 1,300!
在德語中,我們有以下結果
Besucher Nr. 1.300, willkommen bei ACME Corp.!
假設我們要建立 HTML,在表格中顯示下列資料
const
data
=
[
{
first
:
'<Jane>'
,
last
:
'Bond'
},
{
first
:
'Lars'
,
last
:
'<Croft>'
},
];
如前所述,範本文字並非範本
範本基本上是一個函式:輸入資料,輸出文字。這個說明提供了我們一個線索,說明我們如何將範本文字轉換為實際範本。讓我們實作一個範本 tmpl
,作為將陣列 addrs
對應到字串的函式
const
tmpl
=
addrs
=>
`
<table>
${
addrs
.
map
(
addr
=>
`
<tr><td>
${
addr
.
first
}
</td></tr>
<tr><td>
${
addr
.
last
}
</td></tr>
`
).
join
(
''
)
}
</table>
`
;
console
.
log
(
tmpl
(
data
));
// Output:
// <table>
//
// <tr><td><Jane></td></tr>
// <tr><td>Bond</td></tr>
//
// <tr><td>Lars</td></tr>
// <tr><td><Croft></td></tr>
//
// </table>
外部範本文字提供括號 <table>
和 </table>
。在內部,我們會嵌入 JavaScript 程式碼,透過串接字串陣列來產生字串。陣列是透過將每個地址對應到兩個表格列來建立的。請注意,純文字部分 <Jane>
和 <Croft>
沒有正確跳脫。下一節將說明如何透過標記範本來執行此動作。
這是一個對較小的範本化工作有用的快速解決方案。對於較大的工作,您可能需要更強大的解決方案,例如範本化引擎 Handlebars.js 或 React 中使用的 JSX 語法。
致謝:此文字範本處理方法是基於 Claus Reinke 的一個構想。
與上一節中所做的,使用未標籤範本進行 HTML 範本處理相比,標籤範本帶來兩個優點
${}
前面加上驚嘆號,它們可以幫我們跳脫字元。這對於包含需要跳脫的字元(<Jane>
)的名稱是必要的。join()
陣列,這樣我們就不必自己呼叫該方法。然後範本的程式碼如下所示。標籤函數的名稱是 html
const
tmpl
=
addrs
=>
html
`
<table>
${
addrs
.
map
(
addr
=>
html
`
<tr><td>!
${
addr
.
first
}
</td></tr>
<tr><td>!
${
addr
.
last
}
</td></tr>
`
)
}
</table>
`
;
const
data
=
[
{
first
:
'<Jane>'
,
last
:
'Bond'
},
{
first
:
'Lars'
,
last
:
'<Croft>'
},
];
console
.
log
(
tmpl
(
data
));
// Output:
// <table>
//
// <tr><td><Jane></td></tr>
// <tr><td>Bond</td></tr>
//
// <tr><td>Lars</td></tr>
// <tr><td><Croft></td></tr>
//
// </table>
請注意,Jane
和 Croft
周圍的尖括號已跳脫,而 tr
和 td
周圍的尖括號則沒有。
如果您在替換前加上驚嘆號(!${addr.first}
),則它將被 HTML 跳脫。標籤函數檢查替換前面的文字,以確定是否要跳脫。
html
的實作 稍後會顯示。
以下是一個標籤範本字串
tagFunction
`lit1
\
n
${
subst1
}
lit2
${
subst2
}
`
此字串觸發(大致上)以下函數呼叫
tagFunction
([
'lit1\n'
,
' lit2 '
,
''
],
subst1
,
subst2
)
確切的函數呼叫看起來更像這樣
// Globally: add template object to per-realm template map
{
// “Cooked” template strings: backslash is interpreted
const
templateObject
=
[
'lit1\n'
,
' lit2 '
,
''
];
// “Raw” template strings: backslash is verbatim
templateObject
.
raw
=
[
'lit1\\n'
,
' lit2 '
,
''
];
// The Arrays with template strings are frozen
Object
.
freeze
(
templateObject
.
raw
);
Object
.
freeze
(
templateObject
);
__templateMap__
[
716
]
=
templateObject
;
}
// In-place: invocation of tag function
tagFunction
(
__templateMap__
[
716
],
subst1
,
subst2
)
標籤函數接收兩種輸入
' lit2 '
)。範本物件儲存兩個版本的範本字串
\n
等跳脫字元。儲存在 templateObject[0]
等中。templateObject.raw[0]
等中。${}
嵌入在範本字串中的值(例如 subst1
)。替換是動態的,它們會隨著每次呼叫而改變。全域範本物件背後的想法是,同一個標籤範本可能會執行多次(例如在迴圈或函數中)。範本物件使標籤函數能夠快取來自先前呼叫的資料:它可以將從輸入類型 1(範本字串)中衍生的資料放入物件中,以避免重新計算。快取會針對每個領域(想像瀏覽器中的框架)發生。也就是說,每個呼叫位置和領域只有一個範本物件。
讓我們使用下列標籤函式來探討樣板字串數量與替換數量之間的比較。
function
tagFunc
(
templateObject
,
...
substs
)
{
return
{
templateObject
,
substs
};
}
樣板字串數量永遠是替換數量加一。換句話說:每個替換永遠被兩個樣板字串包圍。
templateObject
.
length
===
substs
.
length
+
1
如果替換是字串中的第一個,它會加上一個空樣板字串
> tagFunc`${
'subst'
}
xyz`
{ templateObject: [ '', 'xyz' ], substs: [ 'subst' ] }
如果替換是字串中的最後一個,它會加上一個空樣板字串
> tagFunc`abc${
'subst'
}
`
{ templateObject: [ 'abc', '' ], substs: [ 'subst' ] }
一個空樣板字串會產生一個樣板字串和沒有替換
> tagFunc``
{ templateObject: [ '' ], substs: [] }
樣板字串有兩種解釋方式 – 已煮和未煮。這些解釋會影響跳脫
${
前面的反斜線 (\
) 會阻止它被解釋為替換的開頭。標籤函式 describe
讓我們探討這表示什麼意思。
function
describe
(
tmplObj
,
...
substs
)
{
return
{
Cooked
:
merge
(
tmplObj
,
substs
),
Raw
:
merge
(
tmplObj
.
raw
,
substs
),
};
}
function
merge
(
tmplStrs
,
substs
)
{
// There is always at least one element in tmplStrs
let
result
=
tmplStrs
[
0
];
substs
.
forEach
((
subst
,
i
)
=>
{
result
+=
String
(
subst
);
result
+=
tmplStrs
[
i
+
1
];
});
return
result
;
}
讓我們使用這個標籤函式
> describe`${
3
+
3
}
`
{ Cooked: '6', Raw: '6' }
> describe`\${
3
+
3
}
`
{ Cooked: '${
3
+
3
}
', Raw: '\\${
3
+
3
}
' }
> describe`\\${
3
+
3
}
`
{ Cooked: '\\6', Raw: '\\\\6' }
> describe`\``
{ Cooked: '`', Raw: '\\`' }
正如你所見,只要已煮的解釋有替換或反引號,未煮的解釋也會如此。然而,字串中的所有反斜線都會出現在未煮的解釋中。
反斜線的其他出現會被解釋如下
例如
> describe`\\`
{ Cooked: '\\', Raw: '\\\\' }
> describe`\n`
{ Cooked: '\n', Raw: '\\n' }
> describe`\u{58}`
{ Cooked: 'X', Raw: '\\u{58}' }
總之:反斜線在原始模式中唯一的效果是,它會跳脫替換和反引號。
String.raw
以下是如何實作 String.raw
function
raw
(
strs
,
...
substs
)
{
let
result
=
strs
.
raw
[
0
];
for
(
const
[
i
,
subst
]
of
substs
.
entries
())
{
result
+=
subst
;
result
+=
strs
.
raw
[
i
+
1
];
}
return
result
;
}
我先前示範了 HTML 範本的標籤函式 html
const
tmpl
=
addrs
=>
html
`
<table>
${
addrs
.
map
(
addr
=>
html
`
<tr><td>!
${
addr
.
first
}
</td></tr>
<tr><td>!
${
addr
.
last
}
</td></tr>
`
)
}
</table>
`
;
const
data
=
[
{
first
:
'<Jane>'
,
last
:
'Bond'
},
{
first
:
'Lars'
,
last
:
'<Croft>'
},
];
console
.
log
(
tmpl
(
data
));
// Output:
// <table>
//
// <tr><td><Jane></td></tr>
// <tr><td>Bond</td></tr>
//
// <tr><td>Lars</td></tr>
// <tr><td><Croft></td></tr>
//
// </table>
如果您在替換之前加上驚嘆號 (!${addr.first}
),它會變成 HTML 跳脫。標籤函式會檢查替換之前的文字,以決定是否要跳脫。
以下是 html
的實作
function
html
(
templateObject
,
...
substs
)
{
// Use raw template strings: we don’t want
// backslashes (\n etc.) to be interpreted
const
raw
=
templateObject
.
raw
;
let
result
=
''
;
substs
.
forEach
((
subst
,
i
)
=>
{
// Retrieve the template string preceding
// the current substitution
let
lit
=
raw
[
i
];
// In the example, map() returns an Array:
// If `subst` is an Array (and not a string),
// we turn it into a string
if
(
Array
.
isArray
(
subst
))
{
subst
=
subst
.
join
(
''
);
}
// If the substitution is preceded by an exclamation
// mark, we escape special characters in it
if
(
lit
.
endsWith
(
'!'
))
{
subst
=
htmlEscape
(
subst
);
lit
=
lit
.
slice
(
0
,
-
1
);
}
result
+=
lit
;
result
+=
subst
;
});
// Take care of last template string
result
+=
raw
[
raw
.
length
-
1
];
// (A)
return
result
;
}
範本字串永遠會比替換多一個,這就是為什麼我們需要在 A 行附加最後一個範本字串。
以下是 htmlEscape()
的簡單實作。
function
htmlEscape
(
str
)
{
return
str
.
replace
(
/&/g
,
'&'
)
// first!
.
replace
(
/>/g
,
'>'
)
.
replace
(
/</g
,
'<'
)
.
replace
(
/"/g
,
'"'
)
.
replace
(
/'/g
,
'''
)
.
replace
(
/`/g
,
'`'
);
}
您可以使用這種範本方法執行更多事情
cond?then:else
) 或邏輯或運算子 (||
) 來完成
!
$
{
addr
.
first
?
addr
.
first
:
'(No first name)'
}
!
$
{
addr
.
first
||
'(No first name)'
}
const
theHtml
=
html
`
<div>
Hello!
</div>`
;
第一個非空白字元是 <div>
,表示文字從第 4 欄開始(最左欄是第 0 欄)。標籤函式 html
可以自動移除所有前置欄位。然後,先前的標籤範本會等於
const
theHtml
=
html
`<div>
Hello!
</div>`
;
// Without destructuring
$
{
addrs
.
map
((
person
)
=>
html
`
<tr><td>!
${
person
.
first
}
</td></tr>
<tr><td>!
${
person
.
last
}
</td></tr>
`
)}
// With destructuring
$
{
addrs
.
map
(({
first
,
last
})
=>
html
`
<tr><td>!
${
first
}
</td></tr>
<tr><td>!
${
last
}
</td></tr>
`
)}
有兩種建立正規表示式實例的方法。
/^abc$/i
RegExp
建構函式:new RegExp('^abc$', 'i')
如果您使用後者,那是因為您必須等到執行時間,才能取得所有必要的元素。您透過串接三種片段來建立正規表示式
對於 #3,特殊字元(點、方括號等)必須跳脫,而 #1 和 #2 可以逐字使用。正規表示式標籤函式 regex
可以協助執行此任務
const
INTEGER
=
/\d+/
;
const
decimalPoint
=
'.'
;
// locale-specific! E.g. ',' in Germany
const
NUMBER
=
regex
`
${
INTEGER
}
(
${
decimalPoint
}${
INTEGER
}
)?`
;
regex
如下所示
function
regex
(
tmplObj
,
...
substs
)
{
// Static text: verbatim
let
regexText
=
tmplObj
.
raw
[
0
];
for
([
i
,
subst
]
of
substs
.
entries
())
{
if
(
subst
instanceof
RegExp
)
{
// Dynamic regular expressions: verbatim
regexText
+=
String
(
subst
);
}
else
{
// Other dynamic data: escaped
regexText
+=
quoteText
(
String
(
subst
));
}
// Static text: verbatim
regexText
+=
tmplObj
.
raw
[
i
+
1
];
}
return
new
RegExp
(
regexText
);
}
function
quoteText
(
text
)
{
return
text
.
replace
(
/[\\^$.*+?()[\]{}|=!<>:-]/g
,
'\\$&'
);
}
範本字面值和標籤範本字面值是從語言 E 借來的,該語言將此功能稱為 準字面值。
巨集允許您實作具有自訂語法的語言結構。對於語法像 JavaScript 一樣複雜的程式語言,很難提供巨集。此領域的研究正在進行中(請參閱 Mozilla 的 sweet.js)。
雖然巨集在實作子語言方面比標籤範本強大許多,但它們依賴語言的標記化。因此,標籤範本是互補的,因為它們專精於文字內容。
如果我想從外部來源(例如檔案)載入範本字面值,例如 `Hello ${name}!`
呢?
如果您這樣做,表示您濫用了範本字面值。由於範本字面值可以包含任意表達式,而且是字面值,因此從其他地方載入它類似於載入表達式或字串字面值 – 您必須使用 eval()
或類似的方法。
反引號是 JavaScript 中仍然未使用的少數 ASCII 字元之一。內插的語法 ${}
非常常見(Unix shell 等)。
範本字串術語在 ES6 規範建立期間相對較晚才變更。以下是舊術語