การอ่านค่าลิเทอรัลด้วยสตริงเทมเพลต ES6

แอดดี้ ออสมานี
แอดดี ออสมานี

ก่อนหน้านี้สตริงใน JavaScript เคยถูกจำกัด ทำให้ขาดความสามารถที่อาจเกิดจากภาษาอย่างเช่น Python หรือ Ruby โดยพื้นฐานแล้ว สตริงเทมเพลต ES6 (พร้อมใช้งานใน Chrome 41 ขึ้นไป) พวกเขาแนะนำวิธีกำหนดสตริงด้วยภาษาเฉพาะโดเมน (DSL) ซึ่งให้ผลดีกว่าดังนี้

  • การประมาณค่าในช่วงสตริง
  • นิพจน์ที่ฝัง
  • สตริงหลายบรรทัดที่ไม่มีการแฮ็ก
  • การจัดรูปแบบสตริง
  • การติดแท็กสตริงเพื่อการ Escape HTML ที่ปลอดภัย การแปลให้เหมาะกับท้องถิ่น และอื่นๆ

แทนที่จะใส่คุณลักษณะอื่นลงในสตริงตามที่เรารู้จักในปัจจุบัน สตริงเทมเพลตนำเสนอวิธีใหม่ในการแก้ปัญหาเหล่านี้อย่างสิ้นเชิง

ไวยากรณ์

สตริงเทมเพลตใช้เครื่องหมายแบ็กกราวด์ (``) แทนที่จะเป็นเครื่องหมายคำพูดเดี่ยวหรือเครื่องหมายคำพูดคู่ที่เราใช้กับสตริงปกติ สตริงเทมเพลตอาจเขียนได้ดังนี้

var greeting = `Yo World!`;

จนถึงตอนนี้ สตริงเทมเพลตยังไม่ได้ให้อะไรเรามากกว่าสตริงปกติ มาเปลี่ยนแปลงกัน

การแทนสตริง

ประโยชน์ที่แท้จริงประการแรกคือการแทนที่สตริง การแทนจะทำให้เราสามารถใช้นิพจน์ JavaScript ที่ถูกต้องใดๆ (เช่น การเพิ่มตัวแปร) และภายในเทมเพลต Literal นี้ ผลลัพธ์ที่ได้จะออกมาเป็นส่วนหนึ่งของสตริงเดียวกัน

สตริงเทมเพลตมีตัวยึดตำแหน่งสำหรับการแทนที่สตริงได้โดยใช้ไวยากรณ์ ${ } ดังที่แสดงด้านล่าง

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

// => "Yo, Brendan!"

เนื่องจากการแทนที่สตริงทั้งหมดในสตริงเทมเพลตเป็นนิพจน์ JavaScript เราจึงสามารถแทนที่ชื่อตัวแปรได้มากมาย ตัวอย่างเช่น ด้านล่างนี้เราสามารถใช้การประมาณค่านิพจน์เพื่อฝังคณิตศาสตร์ในบรรทัดที่อ่านได้

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.

นอกจากนี้ยังมีประโยชน์มากสำหรับฟังก์ชันภายในนิพจน์ด้วย

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

${} ทำงานได้ดีกับนิพจน์ทุกประเภท รวมถึงนิพจน์สมาชิกและการเรียกใช้เมธอด

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

หากคุณต้องการเครื่องหมายแบ็กทิกภายในสตริง สามารถใช้อักขระแบ็กสแลชเป็นอักขระหลีกได้โดยใช้อักขระแบ็กสแลช \ ดังนี้

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

สตริงหลายบรรทัด

สตริงหลายบรรทัดใน JavaScript ต้องมีวิธีแก้ปัญหาเบื้องต้นจากการแฮ็กมาระยะหนึ่งแล้ว โซลูชันปัจจุบันสำหรับพารามิเตอร์นี้กำหนดให้สตริงต้องมีอยู่ในบรรทัดเดียวหรือแยกเป็นสตริงหลายบรรทัดโดยใช้ \ (แบ็กสแลช) ก่อนบรรทัดใหม่แต่ละบรรทัด เช่น

var greeting = "Yo \
World";

แม้วิธีการนี้ควรใช้งานได้ดีในเครื่องมือ JavaScript ที่ทันสมัยส่วนใหญ่ แต่ลักษณะการทำงานนั้นก็ยังคงเป็นเพียงขั้นตอนเล็กน้อย คุณสามารถใช้การต่อสตริงเข้าด้วยกันเพื่อสร้างการสนับสนุนหลายบรรทัดปลอมได้ แต่การทำเช่นนี้ก็ทำให้เกิดความต้องการได้เช่นกัน

var greeting = "Yo " +
"World";

สตริงเทมเพลตจะใช้สตริงหลายบรรทัดได้ง่ายขึ้นอย่างมาก เพียงระบุผู้ให้บริการใหม่ตามที่ต้องการและบูม ตัวอย่าง

ช่องว่างภายในไวยากรณ์เครื่องหมายแบ็กทิกจะถือว่าเป็นส่วนหนึ่งของสตริงด้วย

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

เทมเพลตที่ติดแท็ก

ถึงตอนนี้ เราได้พิจารณาการใช้สตริงเทมเพลตสำหรับการแทนที่สตริงและการสร้างสตริงหลายบรรทัด ฟีเจอร์ที่มีประสิทธิภาพอีกอย่างหนึ่งคือเทมเพลตที่ติดแท็ก เทมเพลตที่ติดแท็กเปลี่ยนรูปแบบสตริงเทมเพลตโดยวางชื่อฟังก์ชันก่อนสตริงเทมเพลต เช่น

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

ความหมายของสตริงเทมเพลตที่ติดแท็กแตกต่างจากสตริงปกติอย่างมาก ตามหลักแล้ว สารเหล่านี้คือการเรียกใช้ฟังก์ชันประเภทพิเศษ ซึ่งก็คือ "น้ำตาล" ข้างต้นเป็น

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

สังเกตว่าอาร์กิวเมนต์ (n + 1) สัมพันธ์กับการแทนที่เกิดขึ้นระหว่างรายการที่ n และ (n + 1) ในอาร์เรย์สตริงอย่างไร ซึ่งมีประโยชน์สำหรับทุกๆ อย่าง แต่วิธีการหนึ่งที่ตรงไปตรงมาที่สุดคือการหลบเลี่ยงตัวแปรที่แทรกเข้ามาโดยอัตโนมัติ

ตัวอย่างเช่น คุณสามารถเขียนฟังก์ชันการ Escape HTML ในลักษณะนี้..

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

แสดงผลสตริงที่มีตัวแปรที่เหมาะสมซึ่งแทรกเข้ามา แต่แทนที่อักขระ HTML ที่ไม่ปลอดภัยทั้งหมด มาเริ่มกันเลย ฟังก์ชันการ Escape HTML ของเราจะใช้อาร์กิวเมนต์ 2 ตัว คือ ชื่อผู้ใช้และความคิดเห็น ผลิตภัณฑ์ทั้งสองอาจมีอักขระที่ไม่ปลอดภัยของ HTML (ได้แก่ ', ", <, > และ &) ตัวอย่างเช่น หากชื่อผู้ใช้คือ "Domenic Denicola" และความคิดเห็นคือ "& is a business tag" เราควรให้เอาต์พุตดังนี้

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

โซลูชันเทมเพลตที่ติดแท็กของเราอาจมีการเขียนดังนี้

// 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"

การใช้งานที่เป็นไปได้อื่นๆ ได้แก่ การ Escape อัตโนมัติ การจัดรูปแบบ การแปล และโดยทั่วไปจะใช้แทนที่ซับซ้อนกว่านั้น

// 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}`;

สรุป

สตริงเทมเพลตอยู่ใน Chrome 41 เบต้า ขึ้นไป, IE Tech Preview, Firefox 35+ และ io.js พูดง่ายๆ ก็คือหากต้องการนำไปใช้งานจริงในปัจจุบัน อุปกรณ์นี้รองรับ Transpiler ของ ES6 หลักๆ แล้ว รวมถึง Traceur และ 6to5 ด้วย หากต้องการลองใช้ โปรดดูตัวอย่างสตริงเทมเพลตในที่เก็บตัวอย่างของ Chrome นอกจากนี้คุณอาจสนใจดูค่าเทียบเท่า ES6 ใน ES5 ซึ่งสาธิตวิธีบรรลุเป้าหมายสตริงเทมเพลตการเพิ่มค่าน้ำตาลที่ได้จากการใช้ ES5 ในปัจจุบัน

สตริงเทมเพลตนำความสามารถที่สำคัญหลายอย่างมาสู่ JavaScript ซึ่งรวมถึงวิธีที่ดีขึ้นในการประมาณค่าในช่วงสตริงและนิพจน์ สตริงหลายบรรทัด และความสามารถในการสร้าง DSL ของคุณเอง

ฟีเจอร์ที่สำคัญที่สุดอย่างหนึ่งจากผู้นำนี้คือเทมเพลตที่ติดแท็ก ซึ่งเป็นฟีเจอร์สำคัญในการจัดทำ DSL ดังกล่าว โดยจะรับส่วนต่างๆ ของสตริงเทมเพลตเป็นอาร์กิวเมนต์ จากนั้นคุณจึงตัดสินใจได้ว่าจะใช้สตริงและการแทนที่เพื่อระบุผลลัพธ์สุดท้ายของสตริงอย่างไร

อ่านเพิ่มเติม