ES6躬行記(4)——模板字面量

  模板字面量(Template Literal)是一種可以嵌入表達式的格式化字符串,有別於普通字符串,它使用反引號(`)包裹字符序列,而不是雙引號或單引號。模板字面量包含特定形式的佔位符(${expression}),由美圓符號、大括號以及合法的表達式組成,合法的表達式(expression)能夠是變量、算術或函數調用,甚至還能夠是模板字面量。在ES6引入模板字面量後,就能避免用若干個加號來實現字符串拼接,而改用更爲優雅的語法來替代,下面用新舊兩種方式分別來組合字符串。html

var name = "strick",
  age = 29, str;
str = "My name is \"" + name + "\". My age is " + age + ".";    //傳統拼接方式
str = `My name is "${name}". My age is ${age}.`;             //模板字面量方式

  對比上面兩條賦值語句能夠得出,不管是代碼可讀性仍是簡潔性,新方式都要略勝一籌。由於舊方式中的普通字符串是用雙引號包裹的,因此字符序列中的雙引號要用反斜線(\)轉義。而模板字面量則無需爲雙引號或單引號轉義,但若是出現反引號,那麼就得將其轉義。web

1、佔位符

1)表達式express

  在佔位符內,表達式的計算結果會按照必定的規則轉換成字符串,若是計算結果是數字、布爾值、對象或數組等,那麼就調它們內置的toString()方法;而若是是null或undefined,那麼就用String()函數實現類型轉換,具體以下所示。數組

`${"abc"}`;         //"abc"
`${123}`;           //"123"
`${true}`;          //"true"
`${null}`;          //"null"
`${undefined}`;     //"undefined"
`${{ id: 1 }}`;     //"[object Object]"
`${[1, 2, 3]}`;     //"1,2,3"

  有一點要引發注意,那就是佔位符中的變量必須先聲明(能夠不初始化),不然將拋出未定義的引用錯誤。下面的school變量就沒有預先聲明,而直接在模板字面量中使用。瀏覽器

`I am studying at ${school}.`;        //拋出未定義的引用錯誤

  前面曾提到過佔位符中的表達式能夠是模板字面量,這讓佔位符變得很是靈活,能夠適應更多的場景,處理更爲複雜的問題。例若有這麼一個需求,爲Chrome瀏覽器中的CSS屬性添加瀏覽器前綴-webkit-,能夠像下面這樣編寫。函數

let attr = "border-radius";
function isChrome() {
  return true;              //爲了簡化演示,省略了瀏覽器嗅探邏輯
}
attr = `${isChrome() ? `-webkit-${attr}` : attr}`;
console.log(attr);          //"-webkit-border-radius"

2)做用域spa

  在佔位符中的變量,它的做用域和定義模板字面量時所處的位置有關,而不是調用時的位置。如下面代碼爲例,有3個同名的scope變量,分別定義在全局做用域、outer1()函數和inner()函數中,模板字面量做爲一個實參傳遞給inner()函數,最後在inner()函數中把模板字面量輸出到控制檯。code

var scope = "global";     //全局變量
function outer1() {
  var scope = "outer";
  function inner(str) {
    var scope = "inner";
    console.log(str);
  }
  inner(`current ${scope}`);
}
outer1();               //"current outer"

  根據前面的做用域規則可知,獲得的結果是「current outer」。若是模板字面量所處的做用域中沒有該變量,那麼就會沿着做用域鏈向上搜索,直到全局做用域爲止。在下面的代碼中,註釋了outer2()函數中的scope變量,獲得的結果爲「current global」。htm

var scope = "global";     //全局變量
function outer2() {
  //var scope = "outer";
  function inner(str) {
    var scope = "inner";
    console.log(str);
  }
  inner(`current ${scope}`);
}
outer2();               //"current global"

2、多行字符串

  在ES6以前,若是要建立多行字符串,那麼得像下面這樣間接實現。對象

let multi1 = "first line \n" + 
    "second line \n" + 
    "third line";
let multi2 = "first line \n\
    second line \n\
    third line";

  第一種是將多段字符串用加號拼接,第二種是在換行以前使用反斜線(\)。雖然是兩種實現方法,但二者都須要添加換行符(\n)。而在ES6引入了模板字面量後,就能原生支持多行字符串,不須要再用上述權宜之計了。具體以下所示,既不須要加號和反斜線,也不須要換行符,代碼言簡意賅。

let multi3 = `first line
    second line
    third line`;

  注意,模板字面量能識別空白符,像上面代碼中的多行字符串,其第二行和第三行的開頭都包含了空白符,所以在輸出時都會有縮進。

3、標籤模板

  模板字面量雖然強大,但也有它的侷限性,例以下面兩點:

(1)有可能會遭受XSS(跨站腳本攻擊)攻擊,由於沒法轉義HTML中的特殊字符(例如「<」、「>」等)。

(2)不能替代模板引擎(例如Mustache、Handlebars等),由於沒法在佔位符中使用if、while等語句。

1)調用

  爲了解決上述問題,ES6引入了標籤模板(Tagged Template)。標籤模板並非模板,而是一種特殊方式的函數調用,以下所示。

func`<p>${name}</p><p>${age}</p>`;

  調用func()函數的時候省略了圓括號,函數名後面直接跟模板字面量,這就是標籤模板的調用方式。它通常會包含兩個參數,第一個是由沒有被替換的部分組成的數組,第二個是剩餘參數,包含了全部佔位符中的計算結果。下面是一個完整的標籤模板的示例。

function func(literals, ...substitutions) {
  console.log(literals);          //["<p>", "</p><p>", "</p>", raw]
  console.log(substitutions);     //["strick", 29]
}
var name = "strick",
  age = 29;
func`<p>${name}</p><p>${age}</p>`;

  注意觀察兩個參數的輸出結果。literals數組包含三個元素和一個特殊的raw屬性。三個元素分別是第一個佔位符以前的部分,兩個佔位符之間的部分和第二個佔位符後面的部分。剩餘參數substitutions由兩個佔位符中所引用的變量name和age的值組成。

2)raw屬性

  這裏重點提一下raw屬性,它也是一個數組,包含了literals數組中的三個元素所對應的原始信息,至關於爲每一個元素調用了一次String對象的raw()方法。注意,String.raw()是一個內置的標籤模板,在調用時要用特殊的形式。

  下面用一個例子來演示String.raw()的功能,先定義一個包含水平製表符(\t)的字符串,而後在第一次輸出的時候,「<p>」和「</p>」之間會有空格隔開,接着調用String.raw(),再次輸出時就能把「\t」也一併顯示。其實要在控制檯顯示第二條註釋須要在「\t」前加一條反斜線(即「<p>\\t</p>」)作轉義,這樣才能把「\t」分紅兩個獨立的字符:「\」和「t」,再也不有水平製表符的效果。但此處爲了便於理解,省略了反斜線。

let html = `<p>\t</p>`;
console.log(html);        //"<p>     </p>"
html = String.raw`<p>\t</p>`;
console.log(html);        //"<p>\t</p>"
相關文章
相關標籤/搜索