+ Birleştirme için neden bu kadar kötü?


44

Herkes JavaScript'in sorunlarından birinin dize bitiştirme için +[ example ] kullandığını söyleyip duruyor . Bazıları sorunun kullanmadığını söylüyor, +zorlama türü [önceki örnekteki yorumlara bakınız]. Ancak, güçlü bir şekilde yazılmış diller birleştirme ve zorlama türleri için problemsizce + kullanın. Örneğin, C # 'da:

int number = 1;
MyObject obj = new MyObject();

var result = "Hello" + number + obj;
// is equivalent to
string result = "hello" + number.ToString() + obj.ToString();

Peki neden JavaScript’te dize birleştirme bu kadar büyük bir problem?


17
neden öyle bad. Kimdir Everyone?
Neal,

3
Bence problem + matematiksel bir operatör. Ve + 'nın birleştirme işlevi, bu standart süreci karıştırır. çünkü "Merhaba" + sayı çalışabilirken + "Merhaba" çalışmayabilir.
SoylentGray

3
@Neal, aynı fikirdeyim - bütün bu mistik 'herkesin' tüm bu sorularda kim olduğunu bilmek istiyorum.
GrandmasterB

Birleştirme için + 'yı kullanmak tamamdır. Dinamik yazmayı kullanmak tamamdır. Her ikisini de aynı dilde kullanmak kötüdür ^ H ^ H ^ H belirsizliğe yol açar.
Gerry

6
@Neal - "Herkes" Douglas Crockford olur. “JavaScript: The Good Parts” adlı kitabında “korkunç” olarak listeliyor.
kirk.burleson

Yanıtlar:


68

Bu JavaScript kodunu düşünün:

var a = 10;
var b = 20;
console.log('result is ' + a + b);

Bu giriş yapacak

sonuç 1020

Büyük olasılıkla hangi amaçlanan değildi ve takip etmek zor olabilir.


4
Temel sorunun çok güzel bir örneği: türler (örnekte string + int + int) karıştığında string bitiştirme işlemi iyi tanımlanmamış. İyi tanımlanmış bir anlambilim ile tamamen farklı bir operatöre sahip olmak (Perl's 'gibi.).
Bruce Ediger,

9
Ya da bu çözme Python'un yolu, sarmak için kuvvet olacağını ave biçinde str(a)ve str(b)çağrılar; Aksi takdirde, bir olsun ValueError: cannot concatenate 'str' and 'int'.
Aphex

6
@FrustratedWithFormsDesigner: Parantez ekleyin. 'result is ' + (a + b).
Jon Purdy

18
Mesele şu ki, C # 'da aynı şeyi yapabileceksiniz ve aynı sonucu alacaksınız - System.Console.WriteLine("result is " + 10 + 20);ama hiç kimse C #' dan bundan şikayet etmiyor . Anlamadığım şey bu; JavaScript hakkında daha kafa karıştırıcı kılan nedir?
yapılandırıcı,

7
@configurator: çünkü insanlara C # 'ın mükemmel olduğu ve javascript'in çok kötü olduğu söylenir. İkisi de yanlıştır, ancak bir dilin itibarı sadık kalıyor, algılar özellikle internette çok önemli.
gbjbaanb

43

"Kötü" derken "yanlış" mı demek istiyorsun yoksa "yavaş" mı demek istiyorsun? Dize birleştirme yapmak matematiksel operatörleri kullanma konusunda tartışma tartışmalı bir "yanlış" argümanı, ancak kullanarak o yapılacak bir argüman da var +yapmak çok dize birleştirme çok yavaş olabilir.

Biz bahsetmiyoruz "Hello" + numberbiz defalarca bir döngü içinde kendisine ekleyerek göreceli olarak büyük bir dize kadar bina bahsediyoruz, performansı hakkında konuşurken.

var combined = "";
for (var i = 0; i < 1000000; i++) {
    combined = combined + "hello ";
}

JavaScript'te (ve bu konuda C #) dizeler değişmez. Asla değiştirilemezler, sadece diğer dizelerle değiştirilirler. Muhtemelen değişkeni combined + "hello "doğrudan değiştirmediğinin farkındasınızdır combined- işlem iki dizeyi bir araya getirmenin sonucu olan yeni bir dize oluşturur, ancak daha sonra combineddeğiştirilmesini istiyorsanız değişkene bu yeni dizeyi atamanız gerekir .

Öyleyse bu döngünün yaptığı milyonlarca farklı dize nesnesi yaratmak ve 999.999'unu atmak. Sürekli olarak büyümekte olan birçok dizginin oluşturulması hızlı değildir ve artık çöp toplayıcısının bundan sonra temizlenmesi için yapacak çok işi vardır.

C #, aynı ortamda StringBuilder sınıfı tarafından çözülen sorunla aynı . JavaScript'te, birleştirmek istediğiniz tüm dizelerin bir dizisini oluşturarak ve daha sonra döngüde milyon kez yerine bir kez birleştirerek daha iyi performans elde edersiniz:

var parts = [];
for (var i = 0; i < 1000000; i++) {
    parts.push("hello");
}
var combined = parts.join(" ");

3
Bu mükemmel bir cevap. Sadece soruya cevap vermekle kalmadı aynı zamanda eğitimli.
Charles Sprayberry

3
Yine de demek istediğim bu değil, soru ile tamamen alakasız.
konfigüratör,

4
Üzgünüm. En az 7 kişi sorunuzu anlatıldığı gibi yanlış anlamışa benziyor.
Joel Mueller

9
Yeniden performans: Belki de 2011'de durum buydu, ancak şimdi + operatör yöntemi array.join yönteminden (en azından v8'de) çok daha hızlı. Bu jsperf bakın: jsperf.com/string-concatenation101
UpTheCreek

2
Join yönteminin bir adımda birden fazla parçadan bir dize ürettiği, tek tek birleştirip bir araya getirme ve böylece tüm bu ara dizileri üretme fikriniz?
Angelo.Hannes

14

Yalnızca ekleme ve birleştirme işlemi için + kullanmak, yalnızca sayı ekleyebildiğiniz ve yalnızca dizeleri birleştirmek mümkün değildir. Ancak, Javascript otomatik olarak gerektiği şekilde sayılar ve dizeler arasında dönüşüm yapacaktır;

3 * 5 => 15
"3" * "5" => 15
2 + " fish" => "2 fish"
"3" + "5" => "35"  // hmmm...

Belki daha basit bir ifadeyle, otomatik tür dönüşümünün varlığında "+" nın aşırı yüklenmesi, + işlecinin beklenen ilişkilendirilmesini bozar:

("x" + 1) + 3 != "x" + (1 + 3)

2
Ayrıca, 3 + 5 == 5 + 3"3" + "5" != "5" + "3"
ekin

10

Dize bitiştirme için gündeme getirilen tipik sayı, birkaç sayı etrafında döner:

  1. +sembol aşırı yüklenmesini
  2. basit hatalar
  3. programcılar tembel olmak

1.

Kullanarak ilk ve en önemli konu +dize Ulama için ve ek kullandığınız olmasıdır +dize Ulama için ve ek.

Eğer değişkenler varsa ave bve ayarladığınız c = a + b, türlerine bağlı bir belirsizlik var ave b. Bu belirsizlik sizi sorunu hafifletmek için ek adımlar atmaya zorlar:

Dize Concat:

c = '' + a + b;

İlave:

c = parseFloat(a) + parseFloat(b);

Bu beni ikinci noktaya getiriyor

2.

Farkına varmadan yanlışlıkla bir dizgeye bir değişken dökmek çok kolaydır. Girişinizin bir dize olarak geldiğini unutmak kolaydır:

a = prompt('Pick a number');
b = prompt('Pick a second number');
alert( a + b );

Sezgisel olmayan girdiler üretecektir, çünkü bilgi girişi girdi değerini döndürür.

3.

Yazmaya ihtiyacım var parseFloatya da Number()her zaman yapmak istediğim sıkıcı ve can sıkıcı bir durum. Kendimi dinamik tiplerimi karıştırmayacağımı hatırlayacak kadar akıllı olduğumu düşünüyorum, ama bu , dinamik tiplerimi asla mahvetmediğim anlamına gelmiyor . İşin gerçeği ben dışarı yazın çok tembel olduğum parseFloatya Number()yapmam çünkü ben ek yapmak her zaman çok eklenmesi.

Çözüm?

+Dize birleştirme için tüm diller kullanılmaz . PHP, .ne zaman numara eklemek istediğinizde ve ne zaman dizelere katılmak istediğinizi ayırt etmek için dizeleri birleştirmek için kullanılır.

Dizeleri birleştirmek için herhangi bir simge kullanılabilir, ancak çoğu zaten kullanılır ve çoğaltmayı önlemek için en iyisidir. Benim yolum olsaydı, muhtemelen _dizeleri birleştirmeye ve _değişken isimlerine izin vermemeye çalışırdım .

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.