Tạo giá trị riêng biệt với chuỗi mẫu ES6

Addy Osmani
Addy Osmani

Chuỗi trong JavaScript vốn bị hạn chế về mặt thời gian, do đó thiếu các chức năng mà bạn có thể mong đợi trong các ngôn ngữ như Python hoặc Ruby. Chuỗi mẫu ES6 (có trong Chrome 41 trở lên) về cơ bản thay đổi nội dung đó. Những công cụ này đưa ra cách xác định chuỗi bằng ngôn ngữ cụ thể theo miền (DSL), giúp cải thiện:

  • Loại nội suy chuỗi
  • Biểu thức được nhúng
  • Chuỗi nhiều dòng không có lượt truy cập
  • Định dạng chuỗi
  • Gắn thẻ chuỗi để thoát HTML an toàn, bản địa hoá và nhiều tính năng khác.

Thay vì nhồi thêm một tính năng khác vào Chuỗi như chúng ta biết hiện nay, Chuỗi mẫu giới thiệu một cách hoàn toàn khác để giải quyết những vấn đề này.

Cú pháp

Chuỗi mẫu sử dụng dấu phẩy ngược (``) thay vì dấu ngoặc đơn hoặc dấu ngoặc kép mà chúng ta thường dùng với chuỗi thông thường. Do đó, chuỗi mẫu có thể được viết như sau:

var greeting = `Yo World!`;

Cho đến nay, Chuỗi mẫu không cung cấp cho chúng ta nhiều thông tin hơn các chuỗi thông thường. Hãy thay đổi điều đó.

Thay thế chuỗi

Một trong những lợi ích thực sự đầu tiên của phương thức này là thay thế chuỗi. Phép thay thế cho phép chúng ta nhận bất kỳ biểu thức JavaScript hợp lệ nào (bao gồm cả việc thêm các biến) và bên trong một Template Literal, kết quả sẽ xuất ra dưới dạng một phần của cùng một chuỗi.

Chuỗi mẫu có thể chứa phần giữ chỗ cho việc thay thế chuỗi bằng cú pháp ${ }, như minh hoạ dưới đây:

// Simple string substitution
var name = "Brendan";
console.log(`Yo, ${name}!`);

// => "Yo, Brendan!"

Vì tất cả các phép thay thế chuỗi trong Chuỗi mẫu đều là biểu thức JavaScript, nên chúng ta có thể thay thế nhiều hơn tên biến. Ví dụ: dưới đây, chúng tôi có thể sử dụng nội suy biểu thức để nhúng cho một số phép toán cùng dòng có thể đọc được:

var a = 10;
var b = 10;
console.log(`JavaScript first appeared ${a+b} years ago. Wow!`);

//=> JavaScript first appeared 20 years ago. Wow!

console.log(`The number of JS MVC frameworks is ${2 * (a + b)} and not ${10 * (a + b)}.`);
//=> The number of JS frameworks is 40 and not 200.

Các hàm này cũng rất hữu ích cho các hàm bên trong biểu thức:

function fn() { return "I am a result. Rarr"; }
console.log(`foo ${fn()} bar`);
//=> foo I am a result. Rarr bar.

${} hoạt động tốt với bất kỳ loại biểu thức nào, bao gồm cả biểu thức thành phần và lệnh gọi phương thức:

var user = {name: 'Caitlin Potter'};
console.log(`Thanks for getting this into V8, ${user.name.toUpperCase()}.`);

// => "Thanks for getting this into V8, CAITLIN POTTER";

// And another example
var thing = 'template strings';
console.log(`Say hello to ${thing}.`);

// => Say hello to template strings

Nếu yêu cầu dấu phẩy ngược bên trong chuỗi, thì ký tự thoát có thể thoát bằng cách sử dụng ký tự dấu gạch chéo ngược \ như sau:

var greeting = `\`Yo\` World!`;

Chuỗi nhiều dòng

Chuỗi nhiều dòng trong JavaScript đã yêu cầu phải có giải pháp tấn công trong một thời gian. Các giải pháp hiện tại cho những giải pháp này yêu cầu các chuỗi phải tồn tại trên một dòng hoặc được chia thành các chuỗi nhiều dòng bằng cách sử dụng \ (dấu gạch chéo ngược) trước mỗi dòng mới. Ví dụ:

var greeting = "Yo \
World";

Mặc dù điều này sẽ hoạt động tốt trong hầu hết các công cụ JavaScript hiện đại, nhưng bản thân hành vi này vẫn là một chút tấn công. Người dùng cũng có thể sử dụng nối chuỗi để hỗ trợ nhiều dòng giả mạo, nhưng điều này cũng có nghĩa là bạn đã mong muốn:

var greeting = "Yo " +
"World";

Chuỗi mẫu đơn giản hoá đáng kể các chuỗi nhiều dòng. Bạn chỉ cần thêm các dòng mới nếu cần và nút BOOM. Ví dụ:

Bất kỳ khoảng trắng nào bên trong cú pháp dấu phẩy ngược cũng sẽ được coi là một phần của chuỗi.

console.log(`string text line 1
string text line 2`);

Mẫu được gắn thẻ

Cho đến nay, chúng ta đã xem xét việc sử dụng Chuỗi mẫu để thay thế chuỗi và tạo các chuỗi nhiều dòng. Các mẫu được gắn thẻ cũng có một tính năng mạnh mẽ khác. Mẫu được gắn thẻ biến đổi Chuỗi mẫu bằng cách đặt tên hàm trước chuỗi mẫu. Ví dụ:

fn`Hello ${you}! You're looking ${adjective} today!`

Ngữ nghĩa của chuỗi mẫu được gắn thẻ rất khác so với ngữ nghĩa của một chuỗi mẫu thông thường. Về cơ bản, chúng là một loại lệnh gọi hàm đặc biệt: các lệnh "đơn giản hoá" ở trên

fn(["Hello ", "! You're looking ", " today!"], you, adjective);

Lưu ý cách đối số (n + 1) tương ứng với sự thay thế diễn ra giữa các mục thứ n và (n + 1) trong mảng chuỗi. Điều này có thể hữu ích cho mọi loại vấn đề, nhưng một trong những cách đơn giản nhất là tự động thoát mọi biến nội suy.

Ví dụ: bạn có thể viết một hàm thoát HTML sao cho..

html`<p title="${title}">Hello ${you}!</p>`

sẽ trả về một chuỗi có các biến thích hợp được thay thế, nhưng tất cả các ký tự HTML không an toàn được thay thế. Hãy cùng tiến hành nào! Hàm thoát HTML của chúng ta sẽ nhận hai đối số: tên người dùng và nhận xét. Cả hai đều có thể chứa các ký tự HTML không an toàn (cụ thể là ', ", <, > và &). Ví dụ: nếu tên người dùng là "Domenic Denicola" và nhận xét là "& is a fun tag", chúng ta sẽ xuất:

<b>Domenic Denicola says:</b> "&amp; is a fun tag"

Do đó, giải pháp mẫu được gắn thẻ của chúng tôi có thể được viết như sau:

// HTML Escape helper utility
var util = (function () {
    // Thanks to Andrea Giammarchi
    var
    reEscape = /[&<>'"]/g,
    reUnescape = /&(?:amp|#38|lt|#60|gt|#62|apos|#39|quot|#34);/g,
    oEscape = {
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;',
        "'": '&#39;',
        '"': '&quot;'
    },
    oUnescape = {
        '&amp;': '&',
        '&#38;': '&',
        '&lt;': '<',
        '&#60;': '<',
        '&gt;': '>',
        '&#62;': '>',
        '&apos;': "'",
        '&#39;': "'",
        '&quot;': '"',
        '&#34;': '"'
    },
    fnEscape = function (m) {
        return oEscape[m];
    },
    fnUnescape = function (m) {
        return oUnescape[m];
    },
    replace = String.prototype.replace
    ;
    return (Object.freeze || Object)({
    escape: function escape(s) {
        return replace.call(s, reEscape, fnEscape);
    },
    unescape: function unescape(s) {
        return replace.call(s, reUnescape, fnUnescape);
    }
    });
}());

// Tagged template function
function html(pieces) {
    var result = pieces[0];
    var substitutions = [].slice.call(arguments, 1);
    for (var i = 0; i < substitutions.length; ++i) {
        result += util.escape(substitutions[i]) + pieces[i + 1];
    }

    return result;
}

var username = "Domenic Denicola";
var tag = "& is a fun tag";
console.log(html`<b>${username} says</b>: "${tag}"`);
//=> <b>Domenic Denicola says</b>: "&amp; is a fun tag"

Các trường hợp sử dụng khác có thể bao gồm thoát tự động, định dạng, bản địa hoá và các thao tác thay thế phức tạp hơn:

// Contextual auto-escaping
qsa`.${className}`;
safehtml`<a href="${url}?q=${query}" onclick="alert('${message}')" style="color: ${color}">${message}</a>`;

// Localization and formatting
l10n`Hello ${name}; you are visitor number ${visitor}:n! You have ${money}:c in your account!`

// Embedded HTML/XML
jsx`<a href="${url}">${text}</a>` // becomes React.DOM.a({ href: url }, text)

// DSLs for code execution
var childProcess = sh`ps ax | grep ${pid}`;

Tóm tắt

Chuỗi mẫu có trong Chrome 41 beta+, IE Tech Preview, Firefox 35+ và io.js. Trên thực tế, nếu bạn muốn sử dụng chúng trong phiên bản chính thức hiện nay, thì chúng được hỗ trợ trong các bộ chuyển mã ES6 chính, bao gồm cả Traceur và 6to5. Hãy xem mẫu Chuỗi mẫu của chúng tôi trong kho lưu trữ mẫu của Chrome nếu bạn muốn dùng thử. Có thể bạn cũng sẽ quan tâm đến bài viết Tương đương ES6 trong ES5. Tài liệu này minh hoạ cách đạt được một số Chuỗi mẫu có đường mang lại bằng cách sử dụng ES5 hiện nay.

Chuỗi mẫu mang lại nhiều tính năng quan trọng cho JavaScript. Những cải tiến này bao gồm các cách tốt hơn để nội suy chuỗi và biểu thức, chuỗi nhiều dòng và khả năng tạo các DSL của riêng bạn.

Một trong những tính năng quan trọng nhất mà chúng mang lại là các mẫu được gắn thẻ – một tính năng quan trọng để tạo các DSL như vậy. Các chuỗi này nhận các phần của Chuỗi mẫu dưới dạng đối số, sau đó bạn có thể quyết định cách sử dụng các chuỗi và phương thức thay thế để xác định kết quả cuối cùng của chuỗi.

Đọc thêm