JavaScript'te dize enterpolasyonunu nasıl yapabilirim?


535

Bu kodu düşünün:

var age = 3;

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

Dize birleştirme dışında, bir değişkenin değerini bir dizeye eklemenin başka yolları var mı?


7
CoffeeScript'e göz atabilirsiniz: coffeescript.org
dennismonsewicz

1
Diğerleri belirtildiği gibi, kullandığınız yöntem olup bu konuda gitmek için en kolay yolu. Ben dizeleri içinde değişkenlere başvurmak isteriz, ancak javascript (veya başka bir bulma / değiştirme yaklaşımı) birleştirme kullanmanız gerekir. Senin gibi millet muhtemelen bizim iyiliğimiz için PHP'ye biraz fazla bağlıyız.
Lev

4
Underscore.js şablonunu
Jahan

2
Birleştirme enterpolasyon değildir ve kullanıcı enterpolasyonun nasıl yapılacağını sordu. Bence Lev nihayet cevabı verdi, yani bunu yerel JS'de yapmanın bir yolu yok. Bunun neden gerçek bir soru olmadığını anlamıyorum.
gitb

4
Yanıt vermeme izin verilebilirsem, şimdi daha yeni tercümanlar için başlayan bir çözüm var (2015 FF ve Chrome zaten destekliyor): developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… . Örneğin. Show GRAPH of : ${fundCode}-${funcCode} beni alır Show GRAPH of : AIP001-_sma (dize 1 etrafında backticks kullanın .... burada göstermek gibi görünmüyor)
Marcos

Yanıtlar:


536

ES6'dan bu yana, şablon değişmezlerini kullanabilirsiniz :

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

PS ters tırnakların kullanımına dikkat: ``.


3
Neden bu konuda geri gittiklerini merak ediyorum, "$ {age}" enterpolasyon yapmak için yeterli olmaz mıydı?
Limonata Gülüyor

5
@LaughingLemonade Geriye dönük uyumluluk. Bu şekilde uygulandıysa, bu biçimde bir dize yazdıran tüm kod başarısız olur.
thefourtheye

1
@Jonathan Backticks'i her zaman anlamlı bir karakter olarak sevmiyorum. Ancak, nedenin evrensel olup olmadığından veya yalnızca dillerimin klavye düzenleri için geçerli olduğundan emin değilim. Backtick, bir sonraki karaktere kadar yazılmayı bekleyen, yazmak için iki kez vurulmasını gerektiren (ve şu anda iki tanesini yazan) özel bir karakter türüdür. Bunun nedeni, "e" gibi bazı karakterlerin onlarla etkileşime girmesidir. Onlarla "ello" yazmaya çalışırsam èllo'yu alırım. Aynı zamanda biraz rahatsız edici bir şekilde konumlandırılmış (başka bir özel ileri okuma karakteri olan shift + forwardtick), aksine yazmaya geçiş gerektiriyor '.
felix

@felix klavye düzeninize özgü bir düşünce; tarif ettiğin şeye "ölü anahtarlar" denir. Norveççe klavye düzeninde (bulunduğum yer) de mevcutlar, bu da tüm programlama için ABD klavye düzenine geçmemizin bir parçası. (ABD klavyesinde de çok daha kolay olan bir dizi başka karakter var - örneğin [ve] -)
Eivind Eklund

239

tl; Dr.

Varsa, ECMAScript 2015'in Şablon Dizesi Değişmezlerini kullanın.

açıklama

ECMAScript 5 spesifikasyonlarına göre bunu yapmanın doğrudan bir yolu yoktur, ancak ECMAScript 6, spesifikasyonun hazırlanması sırasında yarı değişmez olarak da bilinen şablon dizelerine sahiptir . Bunları şu şekilde kullanın:

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

İçindeki herhangi bir geçerli JavaScript ifadesini kullanabilirsiniz {}. Örneğin:

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

Diğer önemli şey, artık çok satırlı dizeler hakkında endişelenmenize gerek yok. Bunları basitçe şöyle yazabilirsiniz

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

Not: Yukarıda gösterilen tüm şablon dizelerini değerlendirmek için io.js v2.4.0 kullandım. Yukarıda gösterilen örnekleri test etmek için en son Chrome'u da kullanabilirsiniz.

Not: ES6 Teknik Özellikleri şimdi sonlandırılmıştır , ancak henüz tüm büyük tarayıcılar tarafından uygulanmamıştır.
Göre Mozilla Geliştirici Ağı sayfalarında kullanıcı bir Opera, Safari veya Internet Explorer konum ve şimdi bu merak ediyorsanız Firefox 34, Chrome 41, Internet Explorer 12: Bu aşağıdaki sürümlerinde başlayan temel destek için uygulanacak , bu test yatağı herkes buna destek olana kadar oyun oynamak için kullanılabilir.


3
Babeljs kullanıyorsanız kullanılabilir , böylece kod tabanınıza tanıtabilir ve daha sonra desteklemeniz gereken tarayıcılar uyguladıktan sonra transpilasyon adımını bırakabilirsiniz.
ivarni

Standart bir dizgiyi bir şablon dizgisine dönüştürmenin bir yolu var mı? Örneğin, biri görüntüleme amaçlı olarak enterpolasyonlu değerlere ihtiyaç duyan bir çeviri tablosu içeren bir json dosyasına sahipse? İlk çözüm muhtemelen bu durum için iyi çalışıyor, ama genel olarak yeni dize şablon sözdizimi gibi düşünüyorum.
nbering

Bu hala js string enterpolasyonunda ilk arama sonuçlarından biri olduğundan, şimdi genel kullanılabilirliği yansıtacak şekilde güncelleyebilmeniz harika olurdu. "Bunu yapmanın doğrudan bir yolu yok" muhtemelen tarayıcıların çoğu için geçerli değil.
Felix Dombek

2
seyircideki zayıf görme için, `` karakter farklıdır ''. Eğer işe yaramakta zorlanıyorsanız, mezar alıntısını kullandığınızdan emin olun (aka tilted alıntı, geri alıntı) en.wikipedia.org/wiki/Grave_accent . Klavyenizin sol üst köşesinde yer alıyor :)
Quincy

@Quincy Mac klavyesinin sol alt - arka kene olarak da bilinir :)
RB.

192

Douglas Crockford'un Düzeltici JavaScript'i bir String.prototype.supplantişlev içerir . Kısa, tanıdık ve kullanımı kolaydır:

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'in prototipini değiştirmek istemezseniz, onu her zaman bağımsız olarak uyarlayabilir veya başka bir ad alanına veya başka bir yere yerleştirebilirsiniz.


102
Not: Bu, birleştirmeden on kat daha yavaş çalışacaktır.
cllpse

36
Ve yaklaşık üç kat daha fazla tuş vuruşu ve bayt alın.
9.11.2011

8
@roosteronacid - Bu hız düşüşü hakkında biraz perspektif verebilir misiniz? 0.01s ila 0.1s (önemli) veya 0.000000001s ila 0.00000001s (alakasız) gibi mi?
chris

16
@george: Makinemdeki hızlı bir test 7312 ns verdi: "İnek moo, moo, moo diyor!" değişkenleri iletilen nesneden dışarı çeken ve bunları şablon dizesinin sabit parçalarıyla birleştiren önceden derlenmiş bir işlev için Crockford'un yöntemini 111 ns vs kullanarak. Bu Chrome 21. idi.
George

13
Veya şu şekilde kullanın:"The {0} says {1}, {1}, {1}!".supplant(['cow', 'moo'])
Robert Massa

54

Dikkat kelimesi: kendi sınırlayıcılarından kaçmanıza izin vermeyen herhangi bir şablon sisteminden kaçının. Örneğin, supplant()burada belirtilen yöntemi kullanarak aşağıdakileri çıkarmanın bir yolu yoktur .

"{Age} değişkenim sayesinde 3 yaşındayım."

Basit enterpolasyon, küçük müstakil scriptler için işe yarayabilir, ancak genellikle herhangi bir ciddi kullanımı sınırlandıracak bu tasarım hatası ile birlikte gelir. Dürüst olmak gerekirse aşağıdaki gibi DOM şablonları tercih ederim:

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

Ve jQuery manipülasyonunu kullanın: $('#age').text(3)

Alternatif olarak, dize birleştirme işleminden bıktıysanız, her zaman alternatif sözdizimi vardır:

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

4
Yan not: Array.join()doğrudan ( +stil) birleştirme işleminden daha yavaştır , çünkü tarayıcı motorları (düğüm ve bugün JS çalıştıran hemen hemen her şeyi içeren V8'i içerir) bunu büyük ölçüde optimize etti ve doğrudan birleştirme
pilau

1
Supplant yöntemi, bahsettiğiniz dizeyi oluşturmanıza izin verir: {token} yalnızca veri nesnesi adlı bir üye içeriyorsa değiştirilir token- böylece üyesi olan bir veri nesnesi sağlamazsanız age, sorun olmaz.
Chris Fewtrell

1
Chris, bunun nasıl bir çözüm olduğunu anlamıyorum. Hem yaş değişkeni hem de {age} dizesini kullanmak için örneği kolayca güncelleyebilirim. Şablon kopyalama metnine dayalı olarak değişkenlerinizi nelere adlandırabileceğiniz konusunda gerçekten endişelenmek istiyor musunuz? --Ayrıca, bu yazıdan beri veri bağlama kütüphanelerinin büyük bir hayranı oldum. RactiveJS gibi bazıları sizi değişken açıklıklara sahip bir DOM'dan kurtaracaktır. Ve Bıyık'tan farklı olarak, yalnızca sayfanın bir kısmını günceller.
greg.kindel

3
Birincil cevabınız, soru HTML ile etiketlenmemiş olsa bile bu javascript'in tarayıcı ortamında çalıştığını varsayar.
canon

2
supplantİle ilgili "dikkat kelimesi" geçersizdir: "I am 3 years old thanks to my {age} variable.".supplant({}));tam olarak belirtilen dizeyi döndürür. Eğer ageverildiyse, hala basılabilir {ve }kullanılabilir{{age}}
le_m

23

Sprintf kütüphanesini deneyin (tam açık kaynaklı bir JavaScript sprintf uygulaması). Örneğin:

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

vsprintf bir dizi bağımsız değişken alır ve biçimlendirilmiş bir dize döndürür.


22

Henüz doğru bir şekilde nasıl yapılacağını bilmediğimde ve sadece hızlı bir şekilde bir fikir edinmek istediğimde bu deseni birçok dilde kullanıyorum:

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

Özellikle verimli olmasa da okunabilir buluyorum. Her zaman çalışır ve her zaman kullanılabilir:

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

HER ZAMAN

* 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

Dize enterpolasyonu için hafif bir JavaScript modülü olan kivi'yi deneyin .

Yapabilirsin

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

veya

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

6

console.logÇıktıda enterpolasyon yapmak istiyorsanız ,

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

Burada %s"format belirleyici" denir. console.logbu tür enterpolasyon desteğine sahiptir.


1
Tek doğru cevap bu. Soru şablon dizeleriyle değil enterpolasyon ile ilgilidir .
sospedra

5

Değerleri olan bir nesneyi sağlamanızı gerektiren bir çözüm. Bir nesneyi parametre olarak sağlamazsanız, varsayılan olarak global değişkenleri kullanır. Ancak parametreyi kullanmaya daha iyi yapışır, çok daha temizdir.

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
Kullanmayın eval, evalkötülük!
chris97ong

5
@ chris97ong Bu "doğru" olsa da, en azından bir sebep ("kötü" yardımcı olmaz) veya alternatif bir çözüm verin. Kullanmanın neredeyse her zaman bir yolu vardır eval, ancak bazen yoktur. Örneğin, OP, bir arama nesnesini (Groovy enterpolasyonunun nasıl çalıştığı gibi) geçirmek zorunda kalmadan geçerli kapsamı kullanarak enterpolasyon yapmak için bir yol isterse, eminim evalgerekli olacaktır. Sadece eski "eval kötüdür" başvurmak değil.
Ian

1
asla eval kullanmayın ya da asla kullanılmasını önerin
hasen

2
@hasenj bu yüzden "en iyi fikir olmayabilir" dedim ve alternatif bir yöntem sağladı. Ancak maalesef, evalkapsamdaki yerel değişkenlere erişmenin tek yolu budur . Bu yüzden bunu sadece duygularını incittiği için reddetme. BTW, alternatif yolu da tercih ediyorum çünkü daha güvenli, ama evalyöntem OP'nin sorusuna tam olarak cevap veren şey , bu yüzden cevapta.
Lucas Trzesniewski

1
Sorun evalşu ki, erişememe başka bir kapsamdan farklıdır, bu nedenle .interpolateçağrınız global değil, başka bir işlev içindeyse, işe yaramaz.
georg

2

Greg Kindel'in ikinci cevabına genişleyerek, kazan plakasının bazılarını ortadan kaldırmak için bir işlev yazabilirsiniz:

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

Kullanımı:

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

Size bir örnek gösterebilirim:

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'dan beri, nesne anahtarlarında dize enterpolasyonu yapmak istiyorsanız, şöyle bir SyntaxError: expected property name, got '${'şey yaparsanız bir elde edersiniz:

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

Bunun yerine aşağıdakileri yapmalısınız:

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

1

@Chris Nielsen'in gönderisinin ES6 sürümü için daha fazlasını destekleyin .

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

Herkese açık kullanım için HTML oluşturuyorsanız, eski tarayıcılarda şablon sözdiziminin kullanılması başarısız olur. Birleştirme kullanmak, özellikle çok veya uzun ifadeleriniz varsa veya sayı ve dize öğelerinin (her ikisi de + işlecini kullanan) karışımlarını işlemek için parantez kullanmanız gerekiyorsa, zahmetli ve okunması zordur.

PHP değişkenleri içeren alıntılanmış dizeleri ve hatta bazı ifadeleri çok kompakt bir gösterim kullanarak genişletir: $a="the color is $color";

JavaScript'te, aşağıdakileri desteklemek için etkili bir işlev yazılabilir: var a=S('the color is ',color);değişken sayıda bağımsız değişken kullanılarak. Bu örnekte birleştirmeye göre bir avantaj olmamakla birlikte, ifadeler uzadığında bu sözdizimi daha net olabilir. Veya PHP'de olduğu gibi bir JavaScript işlevi kullanarak bir ifadenin başlangıcını belirtmek için dolar işareti kullanılabilir.

Öte yandan, eski tarayıcılar için dizelerin şablon benzeri genişlemesini sağlamak üzere etkili bir geçici çözüm işlevi yazmak zor olmaz. Birisi muhtemelen bunu zaten yapmıştı.

Son olarak, sprintf'in (C, C ++ ve PHP'de olduğu gibi) JavaScript'te yazılabileceğini, ancak bu diğer çözümlerden biraz daha az verimli olacağını hayal ediyorum.


0

Özel esnek enterpolasyon:

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

Şablonlar muhtemelen tanımladığınız durum için en iyiyken, verilerinizi ve / veya bağımsız değişkenlerinizi yinelenebilir / dizi biçiminde varsa veya istiyorsanız, kullanabilirsiniz String.raw.

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

Veriler bir dizi olarak yayılma işlecini kullanabilirsiniz:

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

0

Aradığımı bulamadım, sonra buldum -

Node.js kullanıyorsanız, aşağıdaki utilgibi çalışan bir biçim işlevine sahip yerleşik bir paket vardır:

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

Tesadüfen bu artık console.logNode.js'de de lezzetlere yerleştirilmiştir -

console.log("This really bad error happened: %s", "ReferenceError");
> This really bad error happened: ReferenceError
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.