1. 程式人生 > >ES6模板字面量

ES6模板字面量

用法 延續 有一個 return 表達式 想要 ace 一行 尋找

前面的話

  JS 的字符串相對其他語言來說功能總是有限的,事實上,ES5中一直缺乏許多特性,如多行字符串、字符串格式化、HTML轉義等。ES6通過模板字面量的方式進行了填補,模板字面量試著跳出JS已有的字符串體系,通過一些全新的方法來解決類似的問題。本文將詳細介紹ES6模板字面量

基本用法

  模板字面量是增強版的字符串,它用反引號(`)標識

let message = `Hello world!`;
console.log(message); // "Hello world!"
console.log(typeof message); // "string"
console.log(message.length); //
12

   以上代碼中,使用模板字面量語法創建一個字符串,並賦值給message變量,這時變量的值與一個普通的字符串無異

  如果想在字符串中包含反引號,只需使用反斜杠( \ )轉義即可

let message = `\`Hello\` world!`;
console.log(message); // "`Hello` world!"
console.log(typeof message); // "string"
console.log(message.length); // 14

多行字符串

  自javascript誕生起,開發者們就一直在尋找一種能創建多行字符串的方法。如果使用雙引號或單引號,字符串一定要在同一行才行

【反斜杠】

  由於javascript長期以來一直存在一個語法bug,在換行之前的反斜線( \ )可以承接下一行的代碼,於是可以利用這個bug來創建多行字符串

var message = "Multiline string";
console.log(message); // "Multiline string"

  message 字符串打印輸出時不會有換行,因為反斜線被視為延續符號而不是新行的符號。為了在輸出中顯示換行,需要手動加入換行符

var message = "Multiline \nstring";
// "Multiline 
// string"
console.log(message); 

  在所有主流的 JS 引擎中,此代碼都會輸出兩行,但是該行為被認定為一個 bug ,並且許多開發者都建議應避免這麽做

  在ES6之前,通常都依靠數組或字符串的拼接來創建多行字符串

var message = ["Multiline ","string"].join("\n");
let message = "Multiline \n" +"string";

  JS一直以來都不支持多行字符串,開發者的種種解決方法都不夠完美

【反引號】

  ES6 的模板字面量使多行字符串更易創建,因為它不需要特殊的語法,只需在想要的位置直接換行即可,此處的換行會同步出現在結果中

let message = `Multiline
string`;
// "Multiline
// string"
console.log(message); 
console.log(message.length); // 16

  在反引號之內的所有空白符都是字符串的一部分,因此需要特別留意縮進

let message = `Multiline
                             string`;
// "Multiline
                            // string"
console.log(message); 
console.log(message.length); //24

  以上代碼中,模板字面量第二行前面的所有空白符都被視為字符串自身的一部分

  如果一定要通過適當的縮進來對齊文本,可以考慮在多行模板字面量的第一行空置並在後面的幾行縮進

let html = `
<div>
    <h1>Title</h1>
</div>`.trim();

  以上代碼中,模板字面量的第一行沒有任何文本,第二行才有內容。 HTML標簽的縮進增強了可讀性,之後再調用trim()方法移除了起始的空行

  當然,也可以在模板字面量中使用 \n 來指示換行的插入位置

let message = `Multiline\nstring`;
// "Multiline
// string" 
console.log(message); 
console.log(message.length); // 16

變量占位符

  模板字面量看上去僅僅是普通JS字符串的升級版,但二者之間真正的區別在於模板字面量的變量占位符。變量占位符允許將任何有效的JS表達式嵌入到模板字面量中,並將其結果輸出為字符串的一部分

  變量占位符由起始的 ${ 與結束的 } 來界定,之間允許放入任意的 JS 表達式。最簡單的變量占位符允許將本地變量直接嵌入到結果字符串中

let name = "Nicholas",
message = `Hello, ${name}.`;
console.log(message); // "Hello, Nicholas."

  占位符 ${name} 會訪問本地變量 name ,並將其值插入到 message 字符串中。 message變量會立即保留該占位符的結果

  既然占位符是JS表達式,那麽可替換的就不僅僅是簡單的變量名。可以輕易嵌入運算符、函數調用等

let count = 10,
price = 0.25,
message = `${count} items cost $${(count * price).toFixed(2)}.`;
console.log(message); // "10 items cost $2.50."
function fn() {
  return "Hello World";
}

`foo ${fn()} bar`
// foo Hello World bar

  模板字面量本身也是 JS 表達式,因此可以將模板字面量嵌入到另一個模板字面量內部

let name = "Nicholas",
    message = `Hello, ${
        `my name is ${ name }`
    }.`;
console.log(message); // "Hello, my name is Nicholas."

標簽模板

  模板字面量真正的威力來自於標簽模板,每個模板標簽都可以執行模板字面量上的轉換並返回最終的字符串值。標簽指的是在模板字面量第一個反引號‘`‘前方標註的字符串

let message = tag`Hello world`;

  在這個示例中, tag 就是應用到 `Hello world` 模板字面量上的模板標簽

【定義標簽】

  標簽可以是一個函數,調用時傳入加工過的模板字面量各部分數據,但必須結合每個部分來創建結果。第一個參數是一個數組,包含Javascript解釋過後的字面量字符串,它之後的所有參數都是每一個占位符的解釋值

  標簽函數通常使用不定參數特性來定義占位符,從而簡化數據處理的過程

function tag(literals, ...substitutions) {
  // 返回一個字符串
}

  為了進一步理解傳遞給tag函數的參數,查看以下代碼

let count = 10,
price = 0.25,
message = passthru`${count} items cost $${(count * price).toFixed(2)}.`;

  如果有一個名為passthru()的函數,那麽作為一個模板字面量標簽,它會接受3個參數首先是一個literals數組,包含以下元素

  1、第一個占位符前的空字符串("")

  2、第一、二個占位符之間的字符串(" items cost $")

  3、第二個占位符後的字符串(".")

  下一個參數是變量count的解釋值,傳參為10,它也成為了substitutions數組裏的第一個元素

  最後一個參數是(count*price).toFixed(2)的解釋值,傳參為2.50,它是substitutions數組裏的第二個元素

  [註意]literals裏的第一個元素是一個空字符串,這確保了literals[0]總是字符串的始端,就像literals[literals.length-1]總是字符串的結尾一樣。substitutions的數量總比literals少一個,這也意味著表達式substitutions. Iength === literals. Iength-1的結果總為true

var a = 5;
var b = 10;

tag`Hello ${ a + b } world ${ a * b }`;
// 等同於
tag([‘Hello ‘, ‘ world ‘, ‘‘], 15, 50);

  通過這種模式,我們可以將literals和substitutions兩個數組交織在一起重組結果字符串。先取出literals中的首個元素,再取出substitution中的首個元素,然後交替繼續取出每一個元素,直到字符串拼接完成。於是可以通過從兩個數組中交替取值的方式模擬模板字面量的默認行為

function passthru(literals, ...substitutions) {
    let result = "";
    // 僅使用 substitution 的元素數量來進行循環
    for (let i = 0; i < substitutions.length; i++) {
        result += literals[i];
        result += substitutions[i];
    }
    // 添加最後一個字面量
    result += literals[literals.length - 1];
    return result;
}
let count = 10,
price = 0.25,
message = passthru`${count} items cost $${(count * price).toFixed(2)}.`;
console.log(message); // "10 items cost $2.50."

  這個示例定義了一個passthru標簽,模擬模板字面量的默認行為,展示了一次轉換過程。此處的小竅門是使用substitutions.length來為循環計數

【應用】

  “標簽模板”的一個重要應用,就是過濾HTML字符串,防止用戶輸入惡意內容

var message =
  SaferHTML`<p>${sender} has sent you a message.</p>`;

function SaferHTML(templateData) {
  var s = templateData[0];
  for (var i = 1; i < arguments.length; i++) {
    var arg = String(arguments[i]);

    // Escape special characters in the substitution.
    s += arg.replace(/&/g, "&amp;")
            .replace(/</g, "&lt;")
            .replace(/>/g, "&gt;");

    // Don‘t escape special characters in the template.
    s += templateData[i];
  }
  return s;
}

  上面代碼中,sender變量往往是用戶提供的,經過SaferHTML函數處理,裏面的特殊字符都會被轉義

var sender = ‘<script>alert("abc")</script>‘; // 惡意代碼
var message = SaferHTML`<p>${sender} has sent you a message.</p>`;

console.log(message);// <p>&lt;script&gt;alert("abc")&lt;/script&gt; has sent you a message.</p>

  標簽模板的另一個應用,就是多語言轉換(國際化處理)

i18n`Welcome to ${siteName}, you are visitor number ${visitorNumber}!`
// "歡迎訪問xxx,您是第xxxx位訪問者!"

  模板字符串本身並不能取代模板引擎,因為沒有條件判斷和循環處理功能,但是通過標簽函數,可以自己添加這些功能

// 下面的hashTemplate函數
// 是一個自定義的模板處理函數
var libraryHtml = hashTemplate`
  <ul>
    #for book in ${myBooks}
      <li><i>#{book.title}</i> by #{book.author}</li>
    #end
  </ul>
`;

raw()

  String.raw方法,往往用來充當模板字面量的處理函數,返回一個斜杠都被轉義(即斜杠前面再加一個斜杠)的字符串,對應於替換變量後的模板字面量

let message1 = `Multiline\nstring`,
message2 = String.raw`Multiline\nstring`;
console.log(message1); // "Multiline
// string"
console.log(message2); // "Multiline\\nstring"
String.raw`Hi\n${2+3}!`;
// "Hi\\n5!"

String.raw`Hi\u000A!`;
// ‘Hi\\u000A!‘

  如果原字符串的斜杠已經轉義,那麽String.raw不會做任何處理

String.raw`Hi\\n`// "Hi\\n"

  String.raw方法可以作為處理模板字面量的基本方法,它會將所有變量替換,而且對斜杠進行轉義,方便下一步作為字符串來使用。

  String.raw方法也可以作為正常的函數使用。這時,它的第一個參數,應該是一個具有raw屬性的對象,且raw屬性的值應該是一個數組

String.raw({ raw: ‘test‘ }, 0, 1, 2);// ‘t0e1s2t‘

// 等同於
String.raw({ raw: [‘t‘,‘e‘,‘s‘,‘t‘] }, 0, 1, 2);

ES6模板字面量