Come posso eseguire l'interpolazione di stringhe in JavaScript?


536

Considera questo codice:

var age = 3;

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

Esistono altri modi per inserire il valore di una variabile in una stringa, oltre alla concatenazione di stringhe?



1
Come altri hanno indicato, il metodo che stai utilizzando è il modo più semplice per procedere. Mi piacerebbe poter fare riferimento alle variabili all'interno delle stringhe, ma in JavaScript devi usare la concatenazione (o qualche altro approccio trova / sostituisci). Gente come te e io probabilmente siamo un po 'troppo attaccati a PHP per il nostro bene.
Lev

4
Usa il modello
Jahan

2
La concatenazione non è interpolazione e l'utente ha chiesto come eseguire l'interpolazione. Penso che Lev abbia finalmente fornito la risposta, cioè non c'è modo di farlo in JS nativo. Non capisco perché questa non sia una vera domanda.
gitb,

4
Se potessi essere autorizzato a rispondere, ora esiste una soluzione che inizia per i nuovi interpreti (FF 2015 e Chrome già supportano): developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… . Per esempio. Show GRAPH of : ${fundCode}-${funcCode} mi prende Show GRAPH of : AIP001-_sma (usa i backtick attorno alla stringa 1 .... non sembra essere possibile visualizzare qui)
Marcos,

Risposte:


537

Da ES6, è possibile utilizzare valori letterali modello :

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

PS Si noti l'uso di backticks: ``.


3
Mi chiedo perché siano andati con i backtick su questo, "$ {age}" non sarebbe abbastanza per fare l'interpolazione?
Laughing Lemonade,

5
@LaughingLemonade Compatibilità con le versioni precedenti. Se è stato implementato in questo modo, tutto il codice esistente che stampa una stringa in quel formato fallirà.
thefourtheye il

1
@Jonathan Non mi piace sempre il backtick come personaggio significativo. Ma non sono sicuro che il motivo sia universale o si applichi solo alle configurazioni della tastiera delle mie lingue. Backtick è un tipo speciale di personaggio che attende di essere scritto fino al personaggio successivo, richiedendo che venga colpito due volte per scrivere (e ne scrive due in questo momento). Questo perché alcuni personaggi interagiscono con loro, come "e". Se provo a scrivere "ello" con loro, ottengo èllo``. Inoltre è localizzato in modo un po 'fastidioso (shift + forwardtick, che è un altro personaggio così speciale), poiché richiede il passaggio al tipo, a differenza di ".
Felix,

@felix è un pensiero specifico per il layout della tastiera; quello che stai descrivendo si chiama "chiavi morte". Sono presenti anche nel layout di tastiera norvegese (da dove vengo io), che è parte del motivo per cui passo a un layout di tastiera statunitense per tutta la programmazione. (Ci sono un certo numero di altri personaggi - ad esempio [e] - che sono molto più facili anche su una tastiera americana.)
Eivind Eklund

239

tl; dr

Utilizzare i letterali di stringhe modello ECMAScript 2015, se applicabili.

Spiegazione

Non esiste un modo diretto per farlo, come da specifiche ECMAScript 5, ma ECMAScript 6 ha stringhe modello , che erano anche conosciute come quasi letterali durante la stesura delle specifiche. Usali in questo modo:

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

È possibile utilizzare qualsiasi espressione JavaScript valida all'interno di {}. Per esempio:

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

L'altra cosa importante è che non devi più preoccuparti delle stringhe multilinea. Puoi scriverli semplicemente come

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

Nota: ho usato io.js v2.4.0 per valutare tutte le stringhe di modello mostrate sopra. Puoi anche utilizzare l'ultimo Chrome per testare gli esempi sopra riportati.

Nota: le specifiche ES6 sono ora finalizzate , ma devono ancora essere implementate da tutti i principali browser.
Secondo le pagine di Mozilla Developer Network , questo sarà implementato per il supporto di base a partire dalle seguenti versioni: Firefox 34, Chrome 41, Internet Explorer 12. Se sei un utente di Opera, Safari o Internet Explorer e sei curioso di questo ora , questo banco di prova può essere utilizzato per giocare fino a quando tutti non ricevono supporto per questo.


3
È utilizzabile se usi babeljs , quindi puoi introdurlo nella tua base di codice e successivamente abbandonare il passaggio di transpilation una volta che i browser che ti servono per supportarlo lo implementano.
Ivarni,

C'è un modo per convertire una stringa standard in una stringa modello letterale? Ad esempio, se uno avesse un file json contenente una tabella di traduzione che necessitava di valori interpolati in essi per scopi di visualizzazione? Penso che la prima soluzione probabilmente funzioni bene per questa situazione, ma in genere mi piace la nuova sintassi del modello di stringa.
Nbering

Poiché questo è ancora uno dei primi risultati di ricerca sull'interpolazione di stringhe js, sarebbe bello se tu potessi aggiornarlo per riflettere la disponibilità generale ora. "Non esiste un modo diretto per farlo" probabilmente non è più vero per la maggior parte dei browser.
Felix Dombek,

2
per la scarsa vista del pubblico, il "personaggio è diverso da". Se hai problemi a far funzionare tutto questo, assicurati di usare la citazione grave, (aka citazione inclinata, citazione indietro) en.wikipedia.org/wiki/Grave_accent . È in alto a sinistra sulla tastiera :)
Quincy,

@Quincy In basso a sinistra di una tastiera Mac - noto anche come back-tick :)
RB.

192

Il JavaScript correttivo di Douglas Crockford include una String.prototype.supplantfunzione. È breve, familiare e facile da usare:

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 non vuoi cambiare il prototipo di String, puoi sempre adattarlo per essere autonomo, o posizionarlo in qualche altro spazio dei nomi, o altro.


103
Nota: verrà eseguito dieci volte più lentamente della semplice concatenazione.
cllpse,

36
E prendere circa tre volte più tasti e byte.
ma11hew28,

8
@roosteronacid - Puoi dare qualche prospettiva su quella diminuzione di velocità? Come, da 0,01 a 0,1 s (importante) o da 0,000000001 a 0,00000001s (irrilevante)?
chris,

16
@george: un rapido test sulla mia macchina ha dato 7312 ns per "La mucca dice moo, moo, moo!" usando il metodo di Crockford contro 111 ns per una funzione precompilata che estrae le variabili dall'oggetto passato e le concatena con le parti costanti della stringa del modello. Era Chrome 21.
George,

13
O usalo come:"The {0} says {1}, {1}, {1}!".supplant(['cow', 'moo'])
Robert Massa,

54

Avvertenza: evitare qualsiasi sistema di template che non ti permetta di sfuggire ai suoi delimitatori. Ad esempio, non ci sarebbe modo di produrre quanto segue usando il supplant()metodo menzionato qui.

"Ho 3 anni grazie alla mia variabile {age}."

L'interpolazione semplice può funzionare per piccoli script autonomi, ma spesso viene fornita con questo difetto di progettazione che limiterà qualsiasi uso serio. Preferisco onestamente i modelli DOM, come:

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

E usa la manipolazione di jQuery: $('#age').text(3)

In alternativa, se sei semplicemente stanco della concatenazione di stringhe, c'è sempre una sintassi alternativa:

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

4
Nota a margine: Array.join()è più lento della +concatenazione diretta ( stile), perché i motori di browser (che include V8, che include il nodo e quasi tutto ciò che esegue JS oggi) lo hanno ottimizzato in modo massiccio e c'è una grande differenza a favore della concatenazione diretta
pilau

1
Il metodo supplant ti consente di generare la stringa che menzioni: il {token} viene sostituito solo se l'oggetto dati contiene un membro chiamato token- quindi, a condizione che tu non fornisca un oggetto dati che abbia un agemembro, andrà bene.
Chris Fewtrell,

1
Chris, non vedo come sia una soluzione. Potrei facilmente aggiornare l'esempio per usare sia una variabile age che la stringa {age}. Vuoi davvero preoccuparti di come puoi nominare le tue variabili in base al testo di copia del modello? - Inoltre, da questo post sono diventato un grande fan delle librerie di associazione dati. Alcuni, come RactiveJS, ti salveranno da un DOM carico di intervalli variabili. E a differenza di Moustache, aggiorna solo quella parte della pagina.
greg.kindel,

3
La tua risposta principale sembra supporre che questo javascript sia in esecuzione in un ambiente browser anche se la domanda non è taggata con HTML.
canone

2
La tua "avvertenza" in merito supplantè ingiustificata: "I am 3 years old thanks to my {age} variable.".supplant({}));restituisce esattamente la stringa specificata. Se agefosse dato, si potrebbe ancora stampare {e }usare{{age}}
le_m

23

Prova la libreria sprintf (un'implementazione sprintf JavaScript open source completa). Per esempio:

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

vsprintf accetta una serie di argomenti e restituisce una stringa formattata.


22

Uso questo modello in molte lingue quando non so ancora come farlo correttamente e voglio solo un'idea in fretta:

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

Anche se non particolarmente efficiente, lo trovo leggibile. Funziona sempre ed è sempre disponibile:

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



6

Prova kiwi , un modulo JavaScript leggero per l'interpolazione delle stringhe.

Tu puoi fare

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

o

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

6

Se vuoi interpolare in console.logoutput, allora basta

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

Ecco, %squello che viene chiamato un "identificatore di formato". console.logha questo tipo di supporto di interpolazione integrato.


1
Questa è l'unica risposta giusta. La domanda riguarda l' interpolazione e non le stringhe del modello.
sospedra,

5

Ecco una soluzione che richiede di fornire un oggetto con i valori. Se non si fornisce un oggetto come parametro, verrà utilizzato per impostazione predefinita variabili globali. Ma meglio attenersi all'utilizzo del parametro, è molto più pulito.

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
Non usare eval, evalè male!
chris97ong,

5
@ chris97ong Anche se questo è "vero", almeno fornire una ragione ("male" non aiuta) o una soluzione alternativa. C'è quasi sempre un modo per aggirare l'utilizzo eval, ma a volte no. Ad esempio, se l'OP volesse un modo per interpolare usando l'ambito corrente, senza dover passare un oggetto di ricerca (come il modo in cui funziona l'interpolazione Groovy), sono abbastanza sicuro che evalsarebbe necessario. Non limitarti a ricorrere al vecchio "eval is evil".
Ian,

1
non usare mai eval né suggerirne mai l'uso
hasen

2
@hasenj questo è il motivo per cui ho detto che "potrebbe non essere la migliore idea" e ha fornito un metodo alternativo. Ma sfortunatamente, evalè l'unico modo per accedere alle variabili locali nell'ambito . Quindi non rifiutarlo solo perché fa male ai tuoi sentimenti. A proposito, preferisco anche il modo alternativo perché è più sicuro, ma il evalmetodo è ciò che risponde esattamente alla domanda di OP, quindi è nella risposta.
Lucas Trzesniewski,

1
Il problema evalè che non è possibile accedere a vari vars da un altro ambito, quindi se la .interpolatechiamata è all'interno di un'altra funzione e non globale, non funzionerà.
Georg,

2

Espandendo la seconda risposta di Greg Kindel , è possibile scrivere una funzione per eliminare alcune parti della caldaia:

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

1

Posso mostrarti un esempio:

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

Da ES6, se si desidera eseguire l'interpolazione di stringhe nelle chiavi degli oggetti, si otterrà un SyntaxError: expected property name, got '${'se si esegue qualcosa del tipo:

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

Dovresti invece fare quanto segue:

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

1

Supplenti di più per la versione ES6 del post di @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

L'uso della sintassi del modello non riesce nei browser meno recenti, importante se si sta creando HTML per uso pubblico. L'uso della concatenazione è noioso e difficile da leggere, in particolare se si hanno molte o lunghe espressioni o se è necessario utilizzare le parentesi per gestire miscele di elementi numerici e stringa (entrambi i quali utilizzano l'operatore +).

PHP espande le stringhe tra virgolette contenenti variabili e persino alcune espressioni usando una notazione molto compatta: $a="the color is $color";

In JavaScript, una funzione efficiente può essere scritta per supportare questo:, var a=S('the color is ',color);usando un numero variabile di argomenti. Sebbene in questo esempio non vi sia alcun vantaggio rispetto alla concatenazione, quando le espressioni si allungano, questa sintassi può essere più chiara. Oppure si può usare il simbolo del dollaro per segnalare l'inizio di un'espressione usando una funzione JavaScript, come in PHP.

D'altra parte, scrivere un'efficace funzione alternativa per fornire l'espansione delle stringhe simile a un modello per i browser più vecchi non sarebbe difficile. Qualcuno probabilmente l'ha già fatto.

Infine, immagino che sprintf (come in C, C ++ e PHP) possa essere scritto in JavaScript, anche se sarebbe un po 'meno efficiente di queste altre soluzioni.


0

Interpolazione flessibile personalizzata:

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

Mentre i modelli sono probabilmente i migliori per il caso che descrivi, se hai o desideri i tuoi dati e / o argomenti in forma iterabile / array, puoi usare String.raw.

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

Con i dati come array, è possibile utilizzare l'operatore spread:

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

0

Impossibile trovare quello che cercavo, quindi l'ho trovato -

Se stai usando Node.js, c'è un utilpacchetto integrato con una funzione di formattazione che funziona in questo modo:

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

Per coincidenza, questo è ora integrato anche nei console.logsapori in Node.js -

console.log("This really bad error happened: %s", "ReferenceError");
> This really bad error happened: ReferenceError
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.