Шаблонные литералы (template literals) в ECMAScript 2015 представляют собой синтаксис, позволяющий формировать строки с встроенными выражениями, многострочностью и «сырыми» последовательностями символов. В контексте SWC (Speedy Web Compiler) их обработка является частью этапа трансформации современного JavaScript/TypeScript в совместимый с более старыми средами код.
SWC реализует преобразование шаблонных литералов в эквивалентные конструкции ES5, сохраняя семантику интерполяции, порядок вычислений и особенности спецификации ECMAScript.
Шаблонный литерал в исходном коде:
const name = "Alex";
const message = `Hello, ${name}!`;
Семантически включает:
${…}
Внутреннее представление ECMAScript разбивает шаблон на «квази» (quasis) и выражения:
“Hello,” — шаблонная часть
name — выражение
“!” — завершающая часть
SWC преобразует шаблонные литералы в цепочку конкатенаций строк и выражений. Основная цель — замена синтаксиса ES6 на ES5-совместимый код без изменения порядка вычислений.
Пример:
const a = 10;
const b = 20;
const result = `Sum: ${a + b}`;
var a = 10;
var b = 20;
var result = "Sum: ".concat(a + b);
.concat
SWC предпочитает использование String.prototype.concat
вместо операторов + в ряде случаев оптимизации.
Пример сложного шаблона:
const x = 1;
const y = 2;
const z = 3;
const s = `Values: ${x}, ${y}, ${z}`;
Результат:
var x = 1;
var y = 2;
var z = 3;
var s = "Values: "
.concat(x, ", ")
.concat(y, ", ")
.concat(", ")
.concat(z);
Такой подход снижает количество промежуточных строк и упрощает цепочку операций в runtime.
В зависимости от конфигурации SWC и целевого окружения возможно
использование оператора +:
var s = "Values: " + x + ", " + y + ", " + z;
Выбор стратегии зависит от настроек генерации кода и оптимизаций.
Шаблонные литералы поддерживают переносы строк без специальных символов:
const text = `line1
line2
line3`;
SWC преобразует это в строку с :
var text = "line1\nline2\nline3";
Особенность трансформации заключается в нормализации переносов строк в соответствии с ECMAScript спецификацией.
Выражения внутри ${…} могут включать:
Пример:
const user = { name: "Ann", age: 25 };
const msg = `User: ${user.name}, age: ${user.age * 2}`;
Результат SWC:
var user = { name: "Ann", age: 25 };
var msg = "User: "
.concat(user.name, ", age: ")
.concat(user.age * 2);
Важно, что вычисление выражений происходит до конкатенации, что сохраняет семантику оригинального кода.
Особый случай представляют tagged templates:
function tag(strings, ...values) {
return strings[0] + values[0];
}
const name = "Alex";
const result = tag`Hello ${name}`;
SWC преобразует вызов в функциональную форму:
function tag(strings, ...values) {
return strings[0] + values[0];
}
var name = "Alex";
var result = tag(["Hello ", ""], name);
raw у массива строк
raw у шаблонов
ECMAScript определяет наличие raw-представления строк:
function tag(strings) {
return strings.raw[0];
}
tag`line1\nline2`;
SWC генерирует структуру:
function tag(strings) {
return strings.raw[0];
}
tag(
Object.defineProperties(["line1\nline2"], {
raw: { value: ["line1\\nline2"] }
})
);
Особенности:
raw содержит необработанные escape-последовательности
Object.defineProperties или эквивалентная
конструкция для совместимости
Шаблонные литералы поддерживают escape-последовательности:
const s = `Line1\nLine2`;
SWC преобразует их в строку с реальным переносом:
var s = "Line1\nLine2";
В tagged templates поведение отличается: escape остаются «сырыми» в
raw.
Шаблон:
const s = `a ${f1(f2(x))} b`;
Трансформация:
var s = "a ".concat(f1(f2(x)), " b");
Порядок вычисления строго сохраняется:
f2(x)
f1
SWC не изменяет порядок вызовов, поскольку это критично для семантики JavaScript.
Если шаблон не содержит выражений:
const s = `hello world`;
SWC выполняет свертку в литерал:
var s = "hello world";
Такой случай полностью устраняет накладные расходы на шаблонные механизмы.
В процессе трансформации SWC может объединять соседние литералы:
const s = `a${x}b${y}c`;
Промежуточная форма разбивается на сегменты:
Результат:
var s = "a".concat(x, "b").concat(y, "c");
При подстановке выражений учитывается приведение типов:
const s = `Value: ${{ toString: () => 42 }}`;
Результат:
var s = "Value: ".concat({ toString: function () { return 42; } });
Приведение к строке происходит в runtime через стандартный механизм
ECMAScript ToString.
Трансформация шаблонных литералов влияет на:
.concat или операторов +
Шаблонные литералы могут обрабатываться совместно с:
Пример цепочки трансформаций:
const s = `x: ${(() => 1 + 2)()}`;
После нескольких стадий:
var s = "x: ".concat((function () { return 1 + 2; })());
SWC генерирует код, совместимый с:
“use strict”
При этом исключаются конструкции, недоступные в legacy JavaScript, например:
String.prototype.startsWith (если не включены полифилы)
Object.assign в зависимости от конфигурации
Шаблонные литералы полностью эмулируются без использования нативной поддержки.
Трансформация шаблонных литералов может быть частью общего блока трансформаций:
{
"jsc": {
"transform": {
"templateLiteral": true
}
}
}
В составе полного pipeline:
const s = `${""}`;
Результат:
var s = "".concat("");
const s = `${a}${b}`;
Результат:
var s = "".concat(a, b);
const s = `v:${undefined}`;
Результат:
var s = "v:".concat(undefined);
Приведение к строке выполняется во время выполнения.
Трансформация шаблонных литералов в SWC опирается на формальную модель:
raw через структурированные массивы
Эта модель обеспечивает эквивалентность поведения между исходным ES6-кодом и результирующим ES5-совместимым JavaScript.