Como posso fazer interpolação de strings em JavaScript?

536

Considere este código:

var age = 3;

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

Existem outras maneiras de inserir o valor de uma variável em uma string, além da concatenação da string?

Tom Lehman
fonte
7
Você pode conferir o
dennismonsewicz
1
Como outros indicaram, o método que você está usando é a maneira mais fácil de fazer isso. Eu adoraria poder me referir a variáveis ​​dentro de strings, mas em javascript você precisa usar concatenação (ou alguma outra abordagem de localização / substituição). Pessoas como você e eu provavelmente somos um pouco apegadas ao PHP para nosso próprio bem.
Lev
4
Use Underscore.js template
Jahan
2
A concatenação não é interpolação e o usuário perguntou como fazer a interpolação. Acho que Lev finalmente forneceu a resposta, ou seja, não há como fazer isso no JS nativo. Eu não entendo por que isso não é uma pergunta real.
Gitb # 22/14
4
Se eu pudesse responder, agora existe uma solução iniciada para interpretadores mais recentes (o FF e o Chrome já suportam 2015): developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… . Por exemplo. Show GRAPH of : ${fundCode}-${funcCode} me pega Show GRAPH of : AIP001-_sma (usar acentos graves em torno de corda 1 .... não consigo exibir aqui)
Marcos

Respostas:

537

Desde o ES6, você pode usar literais de modelo :

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

PS Observe o uso de acentos graves: ``.

Damjan Pavlica
fonte
3
Estou me perguntando por que eles concordaram nisso, "$ {age}" não seria suficiente para fazer interpolação?
Laughing Lemonade,
5
@LaughingLemonade Compatibilidade com versões anteriores. Se foi implementado dessa maneira, todo o código existente que imprime uma string nesse formato falhará.
precisa saber é o seguinte
1
@ Jonathan Eu sempre não gosto de backticks como um personagem significativo. Mas não tenho certeza se o motivo é universal ou se aplica apenas aos layouts de teclado dos meus idiomas. Backtick é um tipo especial de caractere que espera ser escrito até o próximo caractere, exigindo que ele seja pressionado duas vezes para escrever (e grava dois deles no momento). Isso ocorre porque alguns caracteres interagem com eles, como "e". Se eu tentar escrever "ello" com eles, recebo èllo``. Ele também é um tanto irritantemente localizado (shift + forwardtick, que é outro caractere especial à procura de lookahead), pois requer mudança para o tipo, diferente de '.
Felix
@felix é um pensamento específico do layout do teclado; o que você está descrevendo é chamado de "chaves mortas". Eles também estão presentes no layout de teclado norueguês (de onde eu sou), o que é parte do motivo de eu mudar para um layout de teclado americano para toda a programação. (Há vários outros caracteres - por exemplo, [e] - que são muito mais fáceis em um teclado americano.)
Eivind Eklund
239

tl; dr

Use literais de cadeia de modelo do ECMAScript 2015, se aplicável.

Explicação

Não existe uma maneira direta de fazer isso, de acordo com as especificações do ECMAScript 5, mas o ECMAScript 6 possui seqüências de modelos , que também eram conhecidas como quase literais durante a elaboração da especificação. Use-os assim:

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

Você pode usar qualquer expressão JavaScript válida dentro do {}. Por exemplo:

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

A outra coisa importante é que você não precisa mais se preocupar com cadeias de linhas múltiplas. Você pode escrevê-los simplesmente como

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

Nota: Eu usei o io.js v2.4.0 para avaliar todas as sequências de modelos mostradas acima. Você também pode usar o Chrome mais recente para testar os exemplos mostrados acima.

Nota: As especificações ES6 agora estão finalizadas , mas ainda precisam ser implementadas por todos os principais navegadores.
De acordo com as páginas da Mozilla Developer Network , isso será implementado para suporte básico a partir das seguintes versões: Firefox 34, Chrome 41, Internet Explorer 12. Se você é um usuário do Opera, Safari ou Internet Explorer e está curioso sobre isso agora , essa bancada de testes pode ser usada para brincar até que todos obtenham suporte para isso.

thefourtheye
fonte
3
É utilizável se você usar o babeljs , para que você possa introduzi-lo na sua base de código e depois largar a etapa de transpilação quando os navegadores necessários para implementá-lo.
ivarni
Existe uma maneira de converter uma string padrão em uma literal de string de modelo? Por exemplo, se alguém tivesse um arquivo json contendo uma tabela de conversão que precisasse de valores interpolados para fins de exibição? Acho que a primeira solução provavelmente funciona bem para essa situação, mas gosto da nova sintaxe do modelo de string em geral.
N
Como esse ainda é um dos primeiros resultados de pesquisa na interpolação de string js, seria ótimo se você pudesse atualizá-lo para refletir a disponibilidade geral agora. "Não existe uma maneira direta de fazer isso" provavelmente não é mais verdade para a maioria dos navegadores.
Felix Dombek 13/03/19
2
para a falta de visão na platéia, o 'personagem é diferente de'. Se você tiver problemas para fazer isso funcionar, certifique-se de usar a citação grave (também conhecida como citação inclinada, citação anterior) en.wikipedia.org/wiki/Grave_accent . É no canto superior esquerdo do teclado :)
Quincy
@ Quincy Inferior esquerdo de um teclado Mac - também conhecido como back-tick :)
RB.
192

O Remedial JavaScript de Douglas Crockford inclui uma String.prototype.supplantfunção. É curto, familiar e fácil de usar:

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' }));

Se você não quiser alterar o protótipo de String, sempre poderá adaptá-lo para ser autônomo ou colocá-lo em outro espaço para nome, ou o que for.

Chris Nielsen
fonte
103
Nota: Isso será executado dez vezes mais devagar do que apenas concatenar.
Cllpse 11/09/09
36
E pressione três vezes mais pressionamentos de tecla e bytes.
ma11hew28
8
@roosteronacid - Você pode dar uma perspectiva dessa diminuição de velocidade? Como de 0,01s a 0,1s (importante) ou de 0,000000001s a 0,00000001s (irrelevante)?
chris
16
@george: Um teste rápido na minha máquina deu 7312 ns para "A vaca diz moo, moo, moo!" usando o método de Crockford vs 111 ns para uma função pré-compilada que extrai as variáveis ​​do objeto passado e as concatena com as partes constantes da cadeia de modelo. Este foi Chrome 21.
George
13
Ou use-o como:"The {0} says {1}, {1}, {1}!".supplant(['cow', 'moo'])
Robert Massa
54

Palavra de cautela: evite qualquer sistema de modelos que não permita que você escape de seus próprios delimitadores. Por exemplo, não haveria como gerar o seguinte usando o supplant()método mencionado aqui.

"Tenho 3 anos de idade graças à minha variável {age}."

A interpolação simples pode funcionar para pequenos scripts independentes, mas geralmente vem com essa falha de design que limitará qualquer uso sério. Sinceramente, prefiro modelos DOM, como:

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

E use a manipulação do jQuery: $('#age').text(3)

Como alternativa, se você simplesmente está cansado da concatenação de strings, sempre há uma sintaxe alternativa:

var age = 3;
var str = ["I'm only", age, "years old"].join(" ");
greg.kindel
fonte
4
Nota lateral: Array.join()é mais lenta que a +concatenação direta ( estilo), porque os mecanismos do navegador (que incluem V8, que inclui nó e quase tudo que executa o JS hoje) a otimizaram enormemente e há muita diferença a favor da concatenação direta
pilau
1
O método suplante permite que você gere a string mencionada: o {token} será substituído apenas se o objeto de dados contiver um membro chamado token- assim, desde que você não forneça um objeto de dados que possua um agemembro, tudo bem.
21418 Chris Fewtrell
1
Chris, não vejo como isso seja uma solução. Eu poderia facilmente atualizar o exemplo para usar uma variável de idade e a string {age}. Deseja realmente se preocupar com o que pode nomear suas variáveis ​​com base no texto de cópia do modelo? - Além disso, desde este post, eu me tornei um grande fã de bibliotecas de ligação de dados. Alguns, como o RactiveJS, salvarão você de um DOM carregado com extensões variáveis. E, ao contrário do Bigode, ele apenas atualiza essa parte da página.
greg.kindel
3
Sua resposta principal parece assumir que esse javascript está sendo executado em um ambiente de navegador, mesmo que a pergunta não esteja marcada com HTML.
canon
2
Sua "palavra de cautela" a respeito supplanté injustificada: "I am 3 years old thanks to my {age} variable.".supplant({}));retorna exatamente a sequência especificada. Se agefosse dado, ainda era possível imprimir {e }usar #{{age}}
le_m 13/07/16
23

Experimente a biblioteca sprintf (uma implementação completa do sprintf JavaScript de código aberto). Por exemplo:

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

vsprintf pega uma matriz de argumentos e retorna uma string formatada.

NawaMan
fonte
22

Eu uso esse padrão em vários idiomas quando ainda não sei como fazê-lo corretamente e só quero ter uma ideia rapidamente:

// 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')
    ;

Embora não seja particularmente eficiente, acho legível. Sempre funciona, e está sempre disponível:

' 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")

SEMPRE

* 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'
Caverna de Jefferey
fonte
12

Você pode fazer isso facilmente usando o ES6 template stringe transpilar para o ES5 usando qualquer transpilar disponível como o babel.

const age = 3;

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

http://www.es6fiddle.net/im3c3euc/

Mohammad Arif
fonte
6

Experimente o kiwi , um módulo JavaScript leve para interpolação de strings.

Você pode fazer

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

ou

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

Se você deseja interpolar na console.logsaída, basta

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

Aqui %sestá o que é chamado de "especificador de formato". console.logtem esse tipo de suporte de interpolação embutido.


fonte
1
Esta é a única resposta certa. A pergunta é sobre interpolação e não seqüências de caracteres de modelo.
Sospedra
5

Aqui está uma solução que requer que você forneça um objeto com os valores. Se você não fornecer um objeto como parâmetro, o padrão será usar variáveis ​​globais. Mas é melhor usar o parâmetro, é muito mais limpo.

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>

Lucas Trzesniewski
fonte
3
Não use eval,eval é mau!
chris97ong
5
@ chris97ong Embora isso seja "verdade", ao menos dê uma razão ("o mal" não ajuda) ou uma solução alternativa. Quase sempre há uma maneira de usar eval, mas às vezes não. Por exemplo, se o OP quisesse uma maneira de interpolar usando o escopo atual, sem ter que passar por um objeto de pesquisa (como a interpolação do Groovy funciona), tenho certeza evalque seria necessário. Não basta recorrer ao antigo "eval is evil".
Ian
1
nunca use eval nem sugira que seja usado
Hasen
2
@hasenj é por isso que eu disse que "pode ​​não ser a melhor idéia" e forneci um método alternativo. Mas, infelizmente, evalé a única maneira de acessar variáveis ​​locais no escopo . Portanto, não o rejeite apenas porque isso machuca seus sentimentos. BTW, eu também prefiro o caminho alternativo porque é mais seguro, mas o evalmétodo é o que responde com precisão à pergunta do OP, portanto está na resposta.
Lucas Trzesniewski
1
O problema evalé que não é possível acessar vars de outro escopo; portanto, se sua .interpolatechamada estiver dentro de outra função e não global, ela não funcionará.
26915 georg
2

Expandindo a segunda resposta de Greg Kindel , você pode escrever uma função para eliminar parte do padrão:

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

Uso:

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!');
James Ko
fonte
1

Eu posso te mostrar um exemplo:

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'));

shades3002
fonte
1

Desde o ES6, se você deseja fazer uma interpolação de cadeias de caracteres em chaves de objeto, você receberá um SyntaxError: expected property name, got '${'se fizer algo como:

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

Você deve fazer o seguinte:

let obj = { [`${age}`]: 3 }
Yuxuan Chen
fonte
1

Mais um suplemento para a versão ES6 da publicação de @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."
SoEzPz
fonte
0

O uso da sintaxe do modelo falha em navegadores antigos, importante se você estiver criando HTML para uso público. O uso de concatenação é entediante e difícil de ler, principalmente se você tiver muitas expressões ou longas, ou se precisar usar parênteses para manipular misturas de itens de número e sequência de caracteres (os quais usam o operador +).

O PHP expande strings entre aspas contendo variáveis ​​e até algumas expressões usando uma notação muito compacta: $a="the color is $color";

Em JavaScript, uma função eficiente pode ser escrita para suportar isso:, var a=S('the color is ',color);usando um número variável de argumentos. Embora não haja vantagem sobre a concatenação neste exemplo, quando as expressões ficam mais longas, essa sintaxe pode ficar mais clara. Ou pode-se usar o sinal de cifrão para sinalizar o início de uma expressão usando uma função JavaScript, como no PHP.

Por outro lado, escrever uma função de solução eficiente para fornecer expansão de strings de modelo para navegadores mais antigos não seria difícil. Alguém provavelmente já fez isso.

Por fim, imagino que o sprintf (como em C, C ++ e PHP) possa ser escrito em JavaScript, embora seja um pouco menos eficiente do que essas outras soluções.

David Spector
fonte
0

Interpolação flexível personalizada:

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>

vsync
fonte
0

Embora os modelos provavelmente sejam os melhores para o caso que você descreve, se você possui ou deseja seus dados e / ou argumentos em forma de iterável / matriz, você pode usá-lo String.raw.

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

Com os dados como uma matriz, pode-se usar o operador spread:

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

Não foi possível encontrar o que estava procurando e o encontrei -

Se você estiver usando o Node.js, há um utilpacote interno com uma função de formato que funciona assim:

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

Coincidentemente, agora isso também está incluído console.logno Node.js.

console.log("This really bad error happened: %s", "ReferenceError");
> This really bad error happened: ReferenceError
B Fish
fonte