الحصول على الحرفية باستخدام سلاسل نماذج ES6

Addy Osmani
Addy Osmani

كانت سلاسل النصوص في JavaScript محدودة في السابق، ولم تكن تتضمّن الإمكانات التي يمكن أن يتوقعها المرء من لغات مثل Python أو Ruby. تغيّر سلاسل النماذج في ES6 (المتوفّرة في الإصدار 41 من Chrome والإصدارات الأحدث) ذلك بشكل أساسي. وهي توفّر طريقة لتحديد السلاسل باستخدام لغات خاصة بالنطاق (DSL)، ما يؤدي إلى تحسين ما يلي:

  • إدراج سلسلة
  • التعبيرات المضمّنة
  • سلاسل متعددة الأسطر بدون عمليات اختراق
  • تنسيق السلسلة
  • وضع علامات على السلاسل لتشفير HTML الآمن والترجمة والمزيد

بدلاً من إضافة ميزة أخرى إلى السلاسل كما نعرفها اليوم، توفّر سلاسل النماذج طريقة مختلفة تمامًا لحلّ هذه المشاكل.

البنية

تستخدم سلاسل النماذج علامات الاقتباس المزدوجة (``) بدلاً من علامات الاقتباس المفردة أو المزدوجة التي اعتدنا عليها مع السلاسل العادية. وبالتالي، يمكن كتابة سلسلة نموذج على النحو التالي:

var greeting = `Yo World!`;

حتى الآن، لم تقدّم لنا سلاسل النماذج أيّ ميزات إضافية مقارنةً بالسلاسل العادية. لنغيّر ذلك.

استبدال السلسلة

ومن بين المزايا الأولى الحقيقية لهذه الوظائف، استبدال السلسلة. تسمح لنا عملية الاستبدال بأخذ أي تعبير JavaScript صالح (بما في ذلك إضافة متغيّرات) وداخل نموذج حرفي، سيتم عرض النتيجة كجزء من السلسلة نفسها.

يمكن أن تحتوي سلاسل النماذج على عناصر نائبة لاستبدال السلسلة باستخدام بنية ${ }، كما هو موضّح أدناه:

// 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) في صفيف السلاسل. يمكن أن يكون ذلك مفيدًا لجميع أنواع البيانات، ولكن من بين أكثرها وضوحًا هو ترميز أي متغيّرات مُدرَجة تلقائيًا.

على سبيل المثال، يمكنك كتابة دالة لتحويل HTML إلى نص عادي على النحو التالي:

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

تُعيد هذه الدالة سلسلة تم استبدال المتغيّرات المناسبة فيها، ولكن مع استبدال جميع الأحرف غير الآمنة في HTML. لنفعل ذلك. ستستخدِم دالة ترميز HTML وسيطتَين: اسم مستخدم وتعليق. قد يحتوي كلاهما على أحرف HTML غير آمنة (أي ' و" و< و> و &). على سبيل المثال، إذا كان اسم المستخدم هو "Domenic Denicola" والتعليق هو "& is a fun 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"

تشمل الاستخدامات الأخرى المحتملة علامات الهروب التلقائية والتنسيق والترجمة والتعريب وبشكل عام، عمليات الاستبدال الأكثر تعقيدًا:

// 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. من الناحية العملية، إذا أردت استخدامها في مرحلة الإنتاج اليوم، تكون متوافقة مع أدوات تحويل ES6 الرئيسية، بما في ذلك Traceur و6to5. يمكنك الاطّلاع على نموذج سلاسل النصوص في مستودع نماذج Chrome إذا أردت تجربتها. قد تهمّك أيضًا المقالة العناصر المشابهة في ES6 في ES5 التي توضّح كيفية تحقيق بعض التحسينات التي توفّرها سلاسل النماذج باستخدام ES5 اليوم.

توفّر سلاسل النماذج العديد من الإمكانات المهمة في JavaScript. وتشمل هذه الطرق طرقًا أفضل لإجراء الاستبدال في السلاسل والتعبيرات، والسلاسل متعددة الأسطر، وإمكانية إنشاء لغات برمجة منتظمة خاصة بك.

ومن أهم الميزات التي يوفّرها هذان الإطاران هي النماذج المُشار إليها، وهي ميزة أساسية لإنشاء لغات البرمجة المحدودة هذه. وتتلقّى هذه الدوالّ أجزاء من سلسلة نموذج كوسيطات، ويمكنك بعد ذلك تحديد كيفية استخدام السلاسل والاستبدالات لتحديد الإخراج النهائي لسلسلتك.

مراجع إضافية