Comment puis-je effectuer une interpolation de chaîne en JavaScript?

536

Considérez ce code:

var age = 3;

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

Existe-t-il d'autres façons d'insérer la valeur d'une variable dans une chaîne, en dehors de la concaténation de chaîne?

Tom Lehman
la source
7
Vous pouvez commander CoffeeScript: coffeescript.org
dennismonsewicz
1
Comme d'autres l'ont indiqué, la méthode que vous utilisez est la manière la plus simple de procéder. J'adorerais pouvoir faire référence à des variables à l'intérieur de chaînes, mais en javascript, vous devez utiliser la concaténation (ou une autre approche de recherche / remplacement). Des gens comme vous et moi sommes probablement un peu trop attachés à PHP pour notre bien.
Lev
4
Utilisez le modèle
Jahan
2
La concaténation n'est pas une interpolation et l'utilisateur a demandé comment effectuer une interpolation. Je pense que Lev a finalement fourni la réponse, c'est-à-dire qu'il n'y a aucun moyen de le faire en JS natif. Je ne comprends pas pourquoi ce n'est pas une vraie question.
gitb
4
Si je pouvais être autorisé à répondre, il existe maintenant une solution pour les nouveaux interprètes (2015 FF et Chrome prennent déjà en charge): developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… . Par exemple. Show GRAPH of : ${fundCode}-${funcCode} m'obtient Show GRAPH of : AIP001-_sma (utilisez des astuces autour de la chaîne 1 .... ne semble pas s'afficher ici)
Marcos

Réponses:

537

Depuis ES6, vous pouvez utiliser des littéraux de modèle :

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

PS Notez l'utilisation de guillemets obliques: ``.

Damjan Pavlica
la source
3
Je me demande pourquoi ils y sont allés avec des astuces, "$ {age}" ne suffirait-il pas pour faire une interpolation?
Laughing Lemonade
5
@LaughingLemonade Rétrocompatibilité. S'il a été implémenté de cette façon, tout le code existant qui imprime une chaîne dans ce format échouera.
thefourtheye
1
@Jonathan Je n'aime toujours pas les backticks en tant que personnage significatif. Mais je ne sais pas si la raison est universelle ou s'applique uniquement aux dispositions de clavier de ma langue. Le backtick est un type spécial de caractère qui attend d'être écrit jusqu'au caractère suivant, nécessitant qu'il soit frappé deux fois pour écrire (et en écrit deux à ce moment). En effet, certains personnages interagissent avec eux, comme "e". Si j'essaye d'écrire "ello" avec eux, je reçois èllo ''. Il est également quelque peu ennuyeux (shift + forwardtick, qui est un autre caractère spécial d'anticipation), car il nécessite un changement de type, contrairement à '.
felix
@felix c'est une réflexion spécifique à votre disposition de clavier; ce que vous décrivez s'appelle "clés mortes". Ils sont également présents dans la disposition du clavier norvégien (d'où je viens), ce qui explique en partie pourquoi je passe à une disposition de clavier américaine pour toute la programmation. (Il y a un certain nombre d'autres caractères - par exemple [et] - qui sont également beaucoup plus faciles sur un clavier américain.)
Eivind Eklund
239

tl; dr

Utilisez les littéraux de chaîne de modèle d'ECMAScript 2015, le cas échéant.

Explication

Il n'y a aucun moyen direct de le faire, selon les spécifications ECMAScript 5, mais ECMAScript 6 a des chaînes de modèle , qui étaient également connues sous le nom de quasi-littéraux lors de la rédaction de la spécification. Utilisez-les comme ceci:

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

Vous pouvez utiliser n'importe quelle expression JavaScript valide à l'intérieur du {}. Par exemple:

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

L'autre chose importante est que vous n'avez plus à vous soucier des chaînes multi-lignes. Vous pouvez les écrire simplement comme

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

Remarque: J'ai utilisé io.js v2.4.0 pour évaluer toutes les chaînes de modèle illustrées ci-dessus. Vous pouvez également utiliser la dernière version de Chrome pour tester les exemples ci-dessus.

Remarque: Les spécifications ES6 sont maintenant finalisées , mais doivent encore être implémentées par tous les principaux navigateurs.
Selon les pages du Réseau des développeurs Mozilla , cela sera implémenté pour une prise en charge de base à partir des versions suivantes: Firefox 34, Chrome 41, Internet Explorer 12. Si vous êtes un utilisateur d'Opera, Safari ou Internet Explorer et que cela vous intéresse maintenant , ce banc d'essai peut être utilisé pour jouer jusqu'à ce que tout le monde soit soutenu.

thefourtheye
la source
3
Il est utilisable si vous utilisez babeljs , vous pouvez donc l'introduire dans votre base de code et ensuite abandonner l'étape de transpilation une fois que les navigateurs dont vous avez besoin pour le soutenir l'implémentent.
ivarni du
Existe-t-il un moyen de convertir une chaîne standard en un littéral de chaîne de modèle? Par exemple, si l'on avait un fichier json contenant une table de traduction qui nécessitait des valeurs interpolées en eux à des fins d'affichage? Je pense que la première solution fonctionne probablement bien dans cette situation, mais j'aime la nouvelle syntaxe de modèle de chaîne en général.
nbering
Comme il s'agit toujours de l'un des premiers résultats de recherche sur l'interpolation de chaîne js, ce serait formidable si vous pouviez le mettre à jour pour refléter la disponibilité générale maintenant. "Il n'y a pas de moyen direct de le faire" n'est probablement plus vrai pour la majorité des navigateurs.
Felix Dombek
2
pour la mauvaise vue du public, le «personnage est différent de». Si vous rencontrez des difficultés pour que cela fonctionne, assurez-vous d'utiliser la citation grave (aka citation inclinée, citation arrière) en.wikipedia.org/wiki/Grave_accent . C'est en haut à gauche de votre clavier :)
Quincy
@Quincy En bas à gauche d'un clavier Mac - également connu sous le nom de back-tick :)
RB.
192

Remedial JavaScript de Douglas Crockford comprend une String.prototype.supplantfonction. Il est court, familier et facile à utiliser:

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

Si vous ne voulez pas changer le prototype de String, vous pouvez toujours l'adapter pour qu'il soit autonome, ou le placer dans un autre espace de noms, ou autre.

Chris Nielsen
la source
103
Remarque: cela fonctionnera dix fois plus lentement que la simple concaténation.
cllpse le
36
Et prenez environ trois fois plus de touches et d'octets.
ma11hew28
8
@roosteronacid - Pouvez-vous donner une perspective sur cette diminution de la vitesse? Comme, de 0,01 s à 0,1 s (important) ou de 0,00000000001 à 0,00000001 (non pertinent)?
chris
16
@george: Un test rapide sur ma machine a donné 7312 ns pour "La vache dit moo, moo, moo!" en utilisant la méthode de Crockford vs 111 ns pour une fonction précompilée qui extrait les variables de l'objet passé et les concatène avec les parties constantes de la chaîne de modèle. C'était Chrome 21.
George
13
Ou utilisez-le comme:"The {0} says {1}, {1}, {1}!".supplant(['cow', 'moo'])
Robert Massa
54

Attention: évitez tout système de modèles qui ne vous permet pas d'échapper à ses propres délimiteurs. Par exemple, il n'y aurait aucun moyen de sortir ce qui suit en utilisant la supplant()méthode mentionnée ici.

"J'ai 3 ans grâce à ma variable {age}."

L'interpolation simple peut fonctionner pour les petits scripts autonomes, mais est souvent accompagnée de cette faille de conception qui limitera toute utilisation sérieuse. Je préfère honnêtement les modèles DOM, tels que:

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

Et utilisez la manipulation jQuery: $('#age').text(3)

Alternativement, si vous êtes simplement fatigué de la concaténation de chaînes, il existe toujours une syntaxe alternative:

var age = 3;
var str = ["I'm only", age, "years old"].join(" ");
greg.kindel
la source
4
Note latérale: Array.join()est plus lent que la +concaténation directe ( style), car les moteurs de navigateur (qui incluent V8, qui inclut le nœud et presque tout ce qui exécute JS aujourd'hui) l'ont optimisé massivement et il y a beaucoup de différence en faveur de la concaténation directe
Pilau
1
La méthode supplant vous permet de générer la chaîne que vous mentionnez: le {token} n'est remplacé que si l'objet de données contient un membre appelé token- donc à condition que vous ne fournissiez pas d'objet de données qui a un agemembre, ce sera bien.
Chris Fewtrell
1
Chris, je ne vois pas comment c'est une solution. Je pourrais facilement mettre à jour l'exemple pour utiliser à la fois une variable d'âge et la chaîne {age}. Voulez-vous vraiment vous inquiéter de ce que vous pouvez nommer vos variables en fonction du texte de copie du modèle? - De plus, depuis ce post, je suis devenu un grand fan des bibliothèques de liaison de données. Certains, comme RactiveJS, vous sauveront d'un DOM chargé avec des portées variables. Et contrairement à Moustache, il ne met à jour que cette partie de la page.
greg.kindel
3
Votre réponse principale semble supposer que ce javascript fonctionne dans un environnement de navigateur même si la question n'est pas balisée avec HTML.
canon
2
Votre "mot d'avertissement" concernant supplantest injustifié: "I am 3 years old thanks to my {age} variable.".supplant({}));renvoie exactement la chaîne donnée. Si elles ageétaient données, on pourrait toujours imprimer {et }utiliser{{age}}
le_m
23

Essayez la bibliothèque sprintf (une implémentation complète de sprintf JavaScript open source). Par exemple:

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

vsprintf prend un tableau d'arguments et renvoie une chaîne formatée.

NawaMan
la source
22

J'utilise ce modèle dans de nombreuses langues quand je ne sais pas encore comment le faire correctement et je veux juste avoir une idée rapidement:

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

Bien qu'il ne soit pas particulièrement efficace, je le trouve lisible. Il fonctionne toujours et est toujours disponible:

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

TOUJOURS

* 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'
Jefferey Cave
la source
12

Vous pouvez facilement utiliser ES6 template stringet transposer en ES5 en utilisant n'importe quel transpilaire disponible comme babel.

const age = 3;

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

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

Mohammad Arif
la source
6

Essayez le kiwi , un module JavaScript léger pour l'interpolation de chaînes.

Tu peux faire

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

ou

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

Si vous voulez interpoler en console.logsortie, alors juste

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

Voici %sce qu'on appelle un "spécificateur de format". console.loga ce type de support d'interpolation intégré.


la source
1
C'est la seule bonne réponse. La question concerne l' interpolation et non les chaînes de modèle.
sospedra
5

Voici une solution qui vous oblige à fournir un objet avec les valeurs. Si vous ne fournissez pas d'objet comme paramètre, il utilisera par défaut des variables globales. Mais il vaut mieux s'en tenir à l'utilisation du paramètre, c'est beaucoup plus propre.

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
la source
3
Ne pas utiliser eval, evalc'est mal!
chris97ong
5
@ chris97ong Bien que ce soit "vrai", donnez au moins une raison ("le mal" n'aide pas) ou une solution alternative. Il y a presque toujours un moyen de contourner l'utilisation eval, mais parfois non. Par exemple, si l'OP voulait un moyen d'interpoler en utilisant la portée actuelle, sans avoir à passer un objet de recherche (comme le fonctionnement de l'interpolation Groovy), je suis presque sûr que ce evalserait nécessaire. Ne vous contentez pas de recourir à l'ancien "eval is evil".
Ian
1
n'utilisez jamais eval et ne suggérez jamais qu'il soit utilisé
hasen
2
@hasenj c'est pourquoi j'ai dit que "ce n'était peut-être pas la meilleure idée" et j'ai fourni une méthode alternative. Mais malheureusement, evalc'est le seul moyen d'accéder aux variables locales de portée . Alors ne le rejetez pas simplement parce que ça fait mal à vos sentiments. BTW, je préfère également la voie alternative car c'est plus sûr, mais la evalméthode est ce qui répond précisément à la question d'OP, donc c'est dans la réponse.
Lucas Trzesniewski
1
Le problème evalest qu'il ne peut pas accéder aux variables à partir d'une autre étendue, donc si votre .interpolateappel est dans une autre fonction, et non global, cela ne fonctionnera pas.
georg
2

En développant la deuxième réponse de Greg Kindel , vous pouvez écrire une fonction pour éliminer une partie du passe-partout:

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

Usage:

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
la source
1

Je peux vous montrer avec un exemple:

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
la source
1

Depuis ES6, si vous souhaitez effectuer une interpolation de chaîne dans les clés d'objet, vous obtiendrez un SyntaxError: expected property name, got '${'si vous faites quelque chose comme:

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

Vous devriez plutôt faire ce qui suit:

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

Supplantent plus pour ES6 version du poste de Nielsen @ Chris.

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
la source
0

L'utilisation de la syntaxe des modèles échoue dans les anciens navigateurs, ce qui est important si vous créez du HTML pour un usage public. L'utilisation de la concaténation est fastidieuse et difficile à lire, en particulier si vous avez de nombreuses expressions ou des expressions longues, ou si vous devez utiliser des parenthèses pour gérer les mélanges d'éléments de nombre et de chaîne (les deux utilisant l'opérateur +).

PHP étend les chaînes entre guillemets contenant des variables et même certaines expressions en utilisant une notation très compacte: $a="the color is $color";

En JavaScript, une fonction efficace peut être écrite pour supporter ceci:, var a=S('the color is ',color);en utilisant un nombre variable d'arguments. Bien qu'il n'y ait aucun avantage sur la concaténation dans cet exemple, lorsque les expressions s'allongent, cette syntaxe peut être plus claire. Ou on peut utiliser le signe dollar pour signaler le début d'une expression à l'aide d'une fonction JavaScript, comme en PHP.

D'un autre côté, il ne serait pas difficile d'écrire une fonction de contournement efficace pour fournir une extension de type modèle de chaînes pour les navigateurs plus anciens. Quelqu'un l'a probablement déjà fait.

Enfin, j'imagine que sprintf (comme en C, C ++ et PHP) pourrait être écrit en JavaScript, même si ce serait un peu moins efficace que ces autres solutions.

David Spector
la source
0

Interpolation flexible personnalisée:

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
la source
0

Bien que les modèles soient probablement les meilleurs pour le cas que vous décrivez, si vous avez ou voulez vos données et / ou arguments sous forme itérable / tableau, vous pouvez les utiliser String.raw.

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

Avec les données sous forme de tableau, on peut utiliser l'opérateur d'étalement:

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

Impossible de trouver ce que je cherchais, puis je l'ai trouvé -

Si vous utilisez Node.js, il existe un utilpackage intégré avec une fonction de formatage qui fonctionne comme ceci:

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

Par coïncidence, cela est désormais intégré aux console.logsaveurs dans Node.js -

console.log("This really bad error happened: %s", "ReferenceError");
> This really bad error happened: ReferenceError
B Poisson
la source