Как я могу сделать интерполяцию строк в JavaScript?


536

Рассмотрим этот код:

var age = 3;

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

Существуют ли другие способы вставки значения переменной в строку, кроме конкатенации строк?


7
Вы можете
оформить

1
Как указали другие, метод, который вы используете, является самым простым способом сделать это. Мне бы хотелось иметь возможность ссылаться на переменные внутри строк, но в javascript вам нужно использовать конкатенацию (или какой-либо другой подход поиска / замены). Такие люди, как вы и я, вероятно, слишком привязаны к PHP для нашего же блага.
Лев

4
Используйте шаблон
Jahan

2
Конкатенация не является интерполяцией, и пользователь спросил, как сделать интерполяцию. Я думаю, что Лев, наконец, дал ответ, то есть нет способа сделать это в нативном JS. Я не понимаю, почему это не реальный вопрос.
Гитб

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

Ответы:


537

Начиная с ES6, вы можете использовать литералы шаблона :

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

PS Обратите внимание на использование обратных кавычек: ``.


3
Мне интересно, почему они пошли с обратными галочками по этому поводу, разве "$ {age}" не было бы достаточно для интерполяции?
Смеющийся лимонад

5
@LaughingLemonade Обратная совместимость. Если это было реализовано таким образом, весь существующий код, который печатает строку в этом формате, потерпит неудачу.
thefourtheye

1
@Jonathan Мне всегда не нравятся кавычки как значимый персонаж. Но я не уверен, является ли причина универсальной или применима только к моим языковым раскладкам клавиатуры. Backtick - это особый вид символов, который ожидает написания до следующего символа, требуя, чтобы его дважды нажимали для записи (и пишет два из них в это время). Это потому, что некоторые персонажи взаимодействуют с ними, например, «е». Если я попытаюсь написать «ello» с ними, я получу èllo``. Он также несколько раздражает (shift + forwardtick, который является еще одним таким специальным символом прогнозирования), так как он требует перехода к типу, в отличие от '.
Феликс

@felix это мысль, которая специфична для вашей раскладки клавиатуры; то, что вы описываете, называется «мертвые ключи». Они присутствуют и в норвежской раскладке клавиатуры (откуда я), что является одной из причин, по которым я переключаюсь на раскладку клавиатуры США для всех программ. (Есть ряд других символов - например, [и] - которые намного проще на американской клавиатуре.)
Эйвинд Эклунд

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 Developer Network , это будет реализовано для базовой поддержки, начиная со следующих версий: 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, вы всегда можете адаптировать его, чтобы он был автономным, или поместил его в какое-то другое пространство имен или что-то еще.


103
Примечание: это будет работать в десять раз медленнее, чем просто конкатенация.
cllpse

36
И взять в три раза больше нажатий клавиш и байтов.
ma11hew28

8
@roosteronacid - Можете ли вы дать некоторое представление об этом снижении скорости? Например, от 0,01 до 0,1 с (важно) или от 0,000000001 до 0,00000001 (не имеет значения)?
Крис

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
Метод supplant позволяет вам генерировать указанную вами строку: {token} заменяется только в том случае, если объект данных содержит вызываемый член token- таким образом, при условии, что вы не предоставите объект данных, в котором есть ageчлен, все будет в порядке.
Крис Фьютрелл

1
Крис, я не понимаю, как это решение. Я мог бы легко обновить пример, чтобы использовать как переменную age, так и строку {age}. Вы действительно хотите беспокоиться о том, что вы можете назвать ваши переменные на основе текста копии шаблона? - Кроме того, с этого поста я стал большим поклонником библиотек привязки данных. Некоторые, такие как RactiveJS, спасут вас от DOM, загруженного переменными диапазонами. И в отличие от усов, он только обновляет эту часть страницы.
greg.kindel

3
Ваш основной ответ, кажется, предполагает, что этот javascript работает в среде браузера, даже если этот вопрос не помечен HTML.
канон

2
Ваше «предостережение» относительно supplantнеоправданно: "I am 3 years old thanks to my {age} variable.".supplant({}));возвращает именно заданную строку. Если бы ageдали, можно было бы распечатать {и }использовать{{age}}
le_m

23

Попробуйте библиотеку sprintf (полная реализация JavaScript с открытым исходным кодом sprintf). Например:

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

vsprintf принимает массив аргументов и возвращает отформатированную строку.


22

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

// JavaScript
let 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'

21

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

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

Используя прототип, вы можете вызвать interpolate напрямую api.prototypejs.org/language/String/prototype/interpolate (внутренне он использует шаблон)
rvazquezglez


6

Попробуйте kiwi , легкий модуль 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
Это единственный правильный ответ. Вопрос об интерполяции не шаблонных строк.
Соспедра

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что потребуется. Не просто прибегайте к старому "eval is evil".
Ян

1
никогда не используйте eval и никогда не предлагайте его использовать
hasen

2
@hasenj, поэтому я сказал, что это "не самая лучшая идея" и предложил альтернативный метод. Но, к сожалению, evalэто единственный способ получить доступ к локальным переменным в области видимости . Так что не отвергайте это только потому, что это ранит ваши чувства. Кстати, я также предпочитаю альтернативный способ, потому что он безопаснее, но именно этот evalметод точно отвечает на вопрос OP, поэтому он и есть в ответе.
Лукас Тресневский

1
Проблема в evalтом, что он не может получить доступ к переменным из другой области, поэтому, если ваш .interpolateвызов находится внутри другой функции и не является глобальным, он не будет работать.
Георг

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.