Як я можу зробити інтерполяцію рядків у JavaScript?


534

Розглянемо цей код:

var age = 3;

console.log("I'm " + age + " years old!");

Чи є інші способи вставити значення змінної у рядок, крім конкатенації рядків?


7
Ви можете
перевірити

1
Як зазначали інші, метод, який ви використовуєте, це найпростіший спосіб досягти цього. Мені б хотілося, щоб можна було посилатися на змінні всередині рядків, але в JavaScript вам потрібно використовувати конкатенацію (або якийсь інший підхід пошуку / заміни). Такі люди, як ви і я, напевно, просто трохи прихильні до PHP для нашого блага.
Лев

4
Використовуйте шаблон
Джахан

2
Конкатенація - це не інтерполяція, і користувач запитав, як зробити інтерполяцію. Я думаю, що Лев нарешті дав відповідь, тобто немає можливості зробити це в рідному JS. Я не розумію, чому це не справжнє питання.
gitb

4
Якщо мені можна дозволити відповісти, зараз існує рішення, що починається для новіших перекладачів (2015 FF і Chrome вже підтримуються): developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… . Напр. Show GRAPH of : ${fundCode}-${funcCode} отримує мене Show GRAPH of : AIP001-_sma (використовуйте зворотні позначки навколо рядка 1 ...., здається, тут не відображається)
Маркос

Відповіді:


536

З ES6 ви можете використовувати буквені літерали :

const age = 3
console.log(`I'm ${age} years old!`)

PS Зверніть увагу на використання зворотних лапок: ``.


3
Мені цікаво, чому вони з цим підключились, чи "$ {age}" не вистачить для інтерполяції?
Сміється лимонад

5
@LaughingLemonade Зворотка сумісність. Якщо він був реалізований таким чином, весь існуючий код, який друкує рядок у такому форматі, вийде з ладу.
thefourtheye

1
@Jonathan Мені завжди не подобаються задники як значущий персонаж. Але я не впевнений, що причина є універсальною чи стосується лише моїх макетів клавіатури. Backtick - це особливий вид символів, який чекає, коли буде записаний наступний символ, і вимагає його двічі натиснути (і записати два з них в цей час). Це відбувається тому, що деякі символи взаємодіють з ними, наприклад "e". Якщо я спробую написати "ello" з ними, я отримую èllo``. Він також дещо дратівливо розташований (shift + форвард, що є ще одним таким особливим символом пошуку), оскільки він вимагає переходу на тип, на відміну від '.
Фелікс

@felix - це думка, специфічна для розкладки вашої клавіатури; те, що ви описуєте, називається "мертвими ключами". Вони також присутні в норвезькій розкладці клавіатури (звідки я родом), що є частиною причини переходу на розкладку клавіатури США для всіх програмувань. (Є ще ряд інших символів - наприклад, [і] - які набагато простіше і на клавіатурі США.)
Eivind Eklund

239

тл; д-р

Використовуйте лінійні рядки шаблонів рядків ECMAScript 2015, якщо це можливо.

Пояснення

Немає прямого способу зробити це згідно зі специфікаціями ECMAScript 5, але ECMAScript 6 має рядки шаблонів , які також були відомі як квазілітералі під час складання специфікації. Використовуйте їх так:

> var n = 42;
undefined
> `foo${n}bar`
'foo42bar'

Ви можете використовувати будь-яке дійсне вираження JavaScript всередині {}. Наприклад:

> `foo${{name: 'Google'}.name}bar`
'fooGooglebar'
> `foo${1 + 3}bar`
'foo4bar'

Інша важлива річ - вам не доведеться більше турбуватися про багаторядкові рядки. Ви можете записати їх просто як

> `foo
...     bar`
'foo\n    bar'

Примітка. Я використовував io.js v2.4.0 для оцінки всіх рядків шаблону, показаних вище. Ви також можете скористатися останнім Chrome для тестування наведених вище прикладів.

Примітка: Технічні характеристики ES6 зараз доопрацьовані , але їх ще не запровадили всі основні браузери.
Як повідомляють на сторінках Мережі розробників Mozilla , це буде реалізовано для основної підтримки, починаючи з наступних версій: Firefox 34, Chrome 41, Internet Explorer 12. Якщо ви користувач Opera, Safari або Internet Explorer і цікавитесь цим зараз , цей тестовий шар можна використовувати для розігрування, поки кожен не отримає підтримки для цього.


3
Це можна використовувати, якщо ви використовуєте babeljs , тож ви можете ввести його у свою кодову базу, а потім пізніше відмовитись від кроку транспіляції після того, як браузери, які вам потрібні для підтримки, реалізують її.
ivarni

Чи є спосіб перетворити стандартний рядок у буквальний рядок шаблону? Наприклад, якщо у вас був файл json, що містить таблицю перекладу, для якої потрібні значення, інтерпольовані в них для відображення? Я думаю, що перше рішення, ймовірно, добре працює в цій ситуації, але мені подобається новий синтаксис шаблону рядків.
nbering

Оскільки це все ще один з перших результатів пошуку інтерполяції js рядків, було б чудово, якби ви могли оновити його, щоб відобразити загальну доступність зараз. "Немає прямого способу зробити це", мабуть, більше не стосується більшості браузерів.
Фелікс Домбек

2
для поганого зору в аудиторії `характер відрізняється від '. Якщо у вас виникли труднощі з налагодженням цієї роботи, переконайтесь, що ви користуєтесь гробовою цитатою (також нахиленою цитатою, зворотною цитатою) en.wikipedia.org/wiki/Grave_accent . Це вгорі зліва на вашій клавіатурі :)
Квінсі

@Quincy Знизу від клавіатури Mac - також відомий як зворотний галочок :)
RB.

192

Поправочний JavaScript Дугласа Крокфорда включає String.prototype.supplantфункцію. Він короткий, звичний та простий у використанні:

String.prototype.supplant = function (o) {
    return this.replace(/{([^{}]*)}/g,
        function (a, b) {
            var r = o[b];
            return typeof r === 'string' || typeof r === 'number' ? r : a;
        }
    );
};

// Usage:
alert("I'm {age} years old!".supplant({ age: 29 }));
alert("The {a} says {n}, {n}, {n}!".supplant({ a: 'cow', n: 'moo' }));

Якщо ви не хочете змінювати прототип String, ви завжди можете адаптувати його до окремого або розмістити його в якомусь іншому просторі імен чи будь-якому іншому.


102
Примітка. Це буде працювати в десять разів повільніше, ніж просто об'єднання.
cllpse

36
І займіть приблизно втричі більше натискань клавіш і байтів.
ma11hew28

8
@roosteronacid - Чи можете ви дати деяку перспективу щодо зменшення швидкості? Мовляв, від 0,01s до 0,1s (важливо) чи від 0,000000001s до 0,00000001s (не має значення)?
chris

16
@george: Швидкий тест на моїй машині дав 7312 нс для "Корова каже му, му, му!" використовуючи метод Крокфорда проти 111 нс для попередньо складеної функції, яка витягує змінні з переданого об'єкта і з'єднує їх з постійними частинами рядка шаблону. Це був Chrome 21.
Джордж

13
Або використовувати його так:"The {0} says {1}, {1}, {1}!".supplant(['cow', 'moo'])
Роберт Масса

54

Слово обережності: уникайте будь-якої системи шаблонів, яка не дозволяє вам уникати власних роздільників. Наприклад, не було б способу вивести наступне за допомогою supplant()вказаного тут методу.

"Мені 3 роки завдяки моїй змінній {age}."

Проста інтерполяція може працювати для невеликих автономних сценаріїв, але часто має такий недолік дизайну, який обмежить будь-яке серйозне використання. Я чесно віддаю перевагу шаблонам DOM, таких як:

<div> I am <span id="age"></span> years old!</div>

І використовувати маніпуляції jQuery: $('#age').text(3)

Крім того, якщо вам просто просто набридло з'єднання рядків, завжди є альтернативний синтаксис:

var age = 3;
var str = ["I'm only", age, "years old"].join(" ");

4
Бічна примітка: Array.join()повільніше, ніж пряме ( +стильове) конкатенація, тому що двигуни браузерів (до яких належить V8, що включає вузол і майже все, що працює JS сьогодні) оптимізували його масово і є велика різниця на користь прямого конкатенації
pilau

1
Метод заміщення дозволяє вам генерувати згадуваний вами рядок: {токен} замінюється лише у тому випадку, якщо об’єкт даних містить член, який називається token- таким чином за умови, що ви не надаєте об'єкт даних, у якого є ageчлен, буде добре.
Кріс Фетрелл

1
Кріс, я не бачу, як це рішення. Я міг би легко оновити приклад, щоб використовувати і вікову змінну, і рядок {age}. Ви дійсно хочете турбуватися про те, що ви можете назвати свої змінні на основі тексту копіювання шаблону? - Так само, з моменту цієї публікації я став великим шанувальником бібліотек, що зв'язують дані. Деякі, як RactiveJS, врятують вас від завантаженого DOM із змінними проміжками. І на відміну від Вуса, він лише оновлює цю частину сторінки.
greg.kindel

3
Ваша основна відповідь, здається, передбачає, що цей JavaScript працює у середовищі браузера, навіть якщо це питання не позначене HTML.
canon

2
Ваше "обережне слово" щодо не supplantє обґрунтованим: "I am 3 years old thanks to my {age} variable.".supplant({}));повертає саме заданий рядок. Якби ageдали, можна було ще надрукувати {та }використовувати{{age}}
le_m

23

Спробуйте бібліотеку sprintf (повна реалізація sprintf з відкритим кодом). Наприклад:

vsprintf('The first 4 letters of the english alphabet are: %s, %s, %s and %s', ['a', 'b', 'c', 'd']);

vsprintf приймає масив аргументів і повертає відформатований рядок.


21

Ви можете використовувати систему шаблонів прототипу, якщо ви дійсно хочете використовувати кувалду для злому гайки:

var template = new Template("I'm #{age} years old!");
alert(template.evaluate({age: 21}));

За допомогою прототипу ви можете зателефонувати на інтерполят безпосередньо api.prototypejs.org/language/String/prototype/interpolate (для внутрішнього використання шаблону)
rvazquezglez

21

Я використовую цю схему на багатьох мовах, коли я ще не знаю, як це зробити належним чином і просто хочу швидко зрозуміти:

// JavaScript
var stringValue = 'Hello, my name is {name}. You {action} my {relation}.'
    .replace(/{name}/g    ,'Indigo Montoya')
    .replace(/{action}/g  ,'killed')
    .replace(/{relation}/g,'father')
    ;

Хоча це не особливо ефективно, я вважаю це читабельним. Це завжди працює, і його завжди доступно:

' VBScript
dim template = "Hello, my name is {name}. You {action} my {relation}."
dim stringvalue = template
stringValue = replace(stringvalue, "{name}"    ,"Luke Skywalker")     
stringValue = replace(stringvalue, "{relation}","Father")     
stringValue = replace(stringvalue, "{action}"  ,"are")

ВСЕГДА

* COBOL
INSPECT stringvalue REPLACING FIRST '{name}'     BY 'Grendel'
INSPECT stringvalue REPLACING FIRST '{relation}' BY 'Mother'
INSPECT stringvalue REPLACING FIRST '{action}'   BY 'did unspeakable things to'


6

Спробуйте ківі , легкий модуль JavaScript для інтерполяції рядків.

Ви можете зробити

Kiwi.compose("I'm % years old!", [age]);

або

Kiwi.compose("I'm %{age} years old!", {"age" : age});

6

Якщо ви хочете інтерполювати у console.logвихід, то просто

console.log("Eruption 1: %s", eruption1);
                         ^^

Ось, %sщо називається "специфікатором формату". console.logвбудована така підтримка інтерполяції.


1
Це єдина правильна відповідь. Питання стосується інтерполяції, а не рядкових шаблонів.
sospedra

5

Ось рішення, яке вимагає надати об'єкту значення. Якщо ви не надаєте об'єкт як параметр, він за замовчуванням використовує глобальні змінні. Але краще дотримуватися використання параметра, він набагато чистіший.

String.prototype.interpolate = function(props) {
    return this.replace(/\{(\w+)\}/g, function(match, expr) {
        return (props || window)[expr];
    });
};

// Test:

// Using the parameter (advised approach)
document.getElementById("resultA").innerText = "Eruption 1: {eruption1}".interpolate({ eruption1: 112 });

// Using the global scope
var eruption2 = 116;
document.getElementById("resultB").innerText = "Eruption 2: {eruption2}".interpolate();
<div id="resultA"></div><div id="resultB"></div>


3
Не використовуй eval, evalзло!
chris97ong

5
@ chris97ong Хоча це "правда", принаймні вкажіть причину ("зло" не допомагає) або альтернативне рішення. Майже завжди існує спосіб використання eval, але іноді немає. Наприклад, якщо ОП хотіла способу інтерполяції, використовуючи поточну область, без необхідності передавати об'єкт пошуку (наприклад, як працює інтерполяція Groovy), я впевнений, що evalце знадобиться. Не просто вдайтеся до старого "евал - це зло".
Ян

1
ніколи не використовуйте eval і ніколи не
пропонуйте

2
@hasenj саме тому я сказав, що це "може бути не найкращою ідеєю" і запропонував альтернативний метод. Але, на жаль, evalце єдиний спосіб отримати доступ до локальних змінних за обсягом . Тому не відкидайте це лише тому, що це шкодить вашим почуттям. До речі, я також віддаю перевагу альтернативному способу, оскільки це безпечніше, але evalметод - це те, що точно відповідає на питання ОП, отже, це відповідь.
Лукас Тшесневський

1
Проблема evalполягає в тому, що не можна отримати доступ до vars з іншої області, тому якщо ваш .interpolateдзвінок знаходиться в іншій функції, а не глобальній, він не працює.
georg

2

Розкриваючи другу відповідь Грега Кінделя , ви можете написати функцію для усунення частини котла:

var fmt = {
    join: function() {
        return Array.prototype.slice.call(arguments).join(' ');
    },
    log: function() {
        console.log(this.join(...arguments));
    }
}

Використання:

var age = 7;
var years = 5;
var sentence = fmt.join('I am now', age, 'years old!');
fmt.log('In', years, 'years I will be', age + years, 'years old!');

1

Я можу показати вам на прикладі:

function fullName(first, last) {
  let fullName = first + " " + last;
  return fullName;
}

function fullNameStringInterpolation(first, last) {
  let fullName = `${first} ${last}`;
  return fullName;
}

console.log('Old School: ' + fullName('Carlos', 'Gutierrez'));

console.log('New School: ' + fullNameStringInterpolation('Carlos', 'Gutierrez'));


1

Оскільки ES6, якщо ви хочете зробити інтерполяцію рядків в об'єктних ключах, ви отримаєте, SyntaxError: expected property name, got '${'якщо ви зробите щось на зразок:

let age = 3
let obj = { `${age}`: 3 }

Ви повинні зробити наступне:

let obj = { [`${age}`]: 3 }

1

Подайте заявку більше на версію ES6 @Chris Nielsen.

String.prototype.supplant = function (o) {
  return this.replace(/\${([^\${}]*)}/g,
    (a, b) => {
      var r = o[b];
      return typeof r === 'string' || typeof r === 'number' ? r : a;
    }
  );
};

string = "How now ${color} cow? {${greeting}}, ${greeting}, moo says the ${color} cow.";

string.supplant({color: "brown", greeting: "moo"});
=> "How now brown cow? {moo}, moo, moo says the brown cow."

0

Неможливо використовувати синтаксис шаблонів у старих браузерах, що важливо, якщо ви створюєте HTML для загального користування. Використання конкатенації є втомливим і важким для читання, особливо якщо у вас багато чи довгих виразів, або якщо ви повинні використовувати дужки для обробки сумішей елементів та рядків (обидва з яких використовують оператор +).

PHP розширює рядки, що цитуються, містять змінні та навіть деякі вирази, використовуючи дуже компактне позначення: $a="the color is $color";

В JavaScript може бути записана ефективна функція для підтримки цього: var a=S('the color is ',color);використовуючи змінну кількість аргументів. Хоча в цьому прикладі немає переваги перед конкатенацією, коли вирази збільшуються, цей синтаксис може бути зрозумілішим. Або можна використовувати знак долара, щоб сигналізувати про початок виразу за допомогою функції JavaScript, як у PHP.

З іншого боку, написання ефективної обхідної функції для забезпечення шаблонного розширення рядків для старих браузерів не буде важким. Хтось, мабуть, це вже зробив.

Нарешті, я думаю, що sprintf (як у C, C ++ та PHP) можна писати на JavaScript, хоча це було б трохи менш ефективно, ніж ці інші рішення.


0

Спеціальна гнучка інтерполяція:

var sourceElm = document.querySelector('input')

// interpolation callback
const onInterpolate = s => `<mark>${s}</mark>`

// listen to "input" event
sourceElm.addEventListener('input', parseInput) 

// parse on window load
parseInput() 

// input element parser
function parseInput(){
  var html = interpolate(sourceElm.value, undefined, onInterpolate)
  sourceElm.nextElementSibling.innerHTML = html;
}

// the actual interpolation 
function interpolate(str, interpolator = ["{{", "}}"], cb){
  // split by "start" pattern
  return str.split(interpolator[0]).map((s1, i) => {
    // first item can be safely ignored
	  if( i == 0 ) return s1;
    // for each splited part, split again by "end" pattern 
    const s2 = s1.split(interpolator[1]);

    // is there's no "closing" match to this part, rebuild it
    if( s1 == s2[0]) return interpolator[0] + s2[0]
    // if this split's result as multiple items' array, it means the first item is between the patterns
    if( s2.length > 1 ){
        s2[0] = s2[0] 
          ? cb(s2[0]) // replace the array item with whatever
          : interpolator.join('') // nothing was between the interpolation pattern
    }

    return s2.join('') // merge splited array (part2)
  }).join('') // merge everything 
}
input{ 
  padding:5px; 
  width: 100%; 
  box-sizing: border-box;
  margin-bottom: 20px;
}

*{
  font: 14px Arial;
  padding:5px;
}
<input value="Everything between {{}} is {{processed}}" />
<div></div>


0

Хоча шаблони, ймовірно, найкращі для описаного вами випадку, якщо ви маєте або хочете, щоб ваші дані та / або аргументи були в ітерабельній / масивній формі, ви можете використовувати String.raw.

String.raw({
  raw: ["I'm ", " years old!"]
}, 3);

Якщо дані є масивом, можна використовувати оператор розповсюдження:

const args = [3, 'yesterday'];
String.raw({
  raw: ["I'm ", " years old as of ", ""]
}, ...args);

0

Не вдалося знайти те, що я шукав, то знайшов -

Якщо ви використовуєте Node.js, є вбудований utilпакет із функцією форматування, який працює так:

util.format("Hello my name is %s", "Brent");
> Hello my name is Brent

Випадково це тепер вбудовано і в console.logаромати в Node.js -

console.log("This really bad error happened: %s", "ReferenceError");
> This really bad error happened: ReferenceError
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.