JavaScript dizeleri değiştirilemez mi? JavaScript'te bir “dize oluşturucu” ya ihtiyacım var mı?


Yanıtlar:


294

Değişmezler. Dize içindeki bir karakteri benzer bir şeyle değiştiremezsiniz var myString = "abbdef"; myString[2] = 'c'. Dize düzenleme yöntemleri trim, sliceyeni dizeler döndürür.

Aynı şekilde, aynı dizeye iki referansınız varsa, birini değiştirmek diğerini etkilemez

let a = b = "hello";
a = a + " world";
// b is not affected

Ancak, her zaman Ash'in cevabında ne dediğini duydum (Array.join'i birleştirmek için daha hızlı), bu yüzden dizeleri birleştirmenin ve bir StringBuilder'e en hızlı yolu soyutlamanın farklı yöntemlerini test etmek istedim. Bunun doğru olup olmadığını görmek için bazı testler yazdım (değil!).

En hızlı yol olduğuna inandığım şey buydu, ancak bir yöntem çağrısı eklemenin yavaşlatabileceğini düşünmeye devam ettim ...

function StringBuilder() {
    this._array = [];
    this._index = 0;
}

StringBuilder.prototype.append = function (str) {
    this._array[this._index] = str;
    this._index++;
}

StringBuilder.prototype.toString = function () {
    return this._array.join('');
}

İşte performans hızı testleri. Üçü de "Hello diggity dog"yüz bin kez boş bir dizeye birleştirilmesinden oluşan devasa bir dize oluşturur .

Üç tür test oluşturdum

  • kullanılması Array.pushveArray.join
  • Kaçınılması için Dizi dizinini kullanma Array.push, ardındanArray.join
  • Düz dize birleştirme

Sonra onları soyutlayarak aynı üç test yarattı StringBuilderConcat, StringBuilderArrayPushve StringBuilderArrayIndex http://jsperf.com/string-concat-without-sringbuilder/5 güzel bir örnek alabilirsiniz oraya gitmek ve çalıştırma testleri edin. Küçük bir hatayı düzelttim, bu yüzden testler için veriler silindi, yeterli performans verisi olduğunda tabloyu güncelleyeceğim. Eski veri tablosu için http://jsperf.com/string-concat-without-sringbuilder/5 adresine gidin .

Bağlantıyı takip etmek istemiyorsanız, bazı rakamlar (Ma5rch 2018'de son güncelleme). Her testteki sayı 1000 işlem / saniyedir ( daha yüksek daha iyidir )

| Browser          | Index | Push | Concat | SBIndex | SBPush | SBConcat |
---------------------------------------------------------------------------
| Chrome 71.0.3578 | 988   | 1006 | 2902   | 963     | 1008   | 2902     |
| Firefox 65       | 1979  | 1902 | 2197   | 1917    | 1873   | 1953     |
| Edge             | 593   | 373  | 952    | 361     | 415    | 444      |
| Exploder 11      | 655   | 532  | 761    | 537     | 567    | 387      |
| Opera 58.0.3135  | 1135  | 1200 | 4357   | 1137    | 1188   | 4294     | 

Bulgular

  • Günümüzde, tüm yaprak dökmeyen tarayıcılar dize birleştirmeyi iyi idare etmektedir. Array.joinsadece IE 11'e yardımcı olur

  • Genel olarak, Opera en hızlı, Array'den 4 kat daha hızlıdır. Katıl

  • Firefox ikinci ve Array.joinFF'de biraz daha yavaş, ancak Chrome'da oldukça yavaş (3x).

  • Chrome üçüncü, ancak dize concat Array'den 3 kat daha hızlı.

  • Bir StringBuilder oluşturmak, performansı çok fazla etkilemez.

Umarım başka biri bunu faydalı bulur

Farklı Test Çantası

@RoyTinker testimin kusurlu olduğunu düşündüğünden, aynı dizeyi birleştirerek büyük bir dize oluşturmayan yeni bir vaka oluşturdum, her yineleme için farklı bir karakter kullanıyor. Dize birleştirme hala daha hızlı veya daha hızlı görünüyordu. Haydi şu testleri yapalım.

Herkesin bunu test etmenin başka yollarını düşünmeye devam etmesini ve aşağıda farklı test örneklerine yeni bağlantılar eklemekten çekinmeyin.

http://jsperf.com/string-concat-without-sringbuilder/7


@Juan, ziyaret etmemizi istediğiniz bağlantı, 112 karakterlik bir dizeyi 30 kez birleştirir. İşte şeyleri dengelemeye yardımcı olabilecek başka bir test - 20.000 farklı 1 karakterlik dizede Array.join vs string birleştirme (birleştirme IE / FF'de çok daha hızlıdır). jsperf.com/join-vs-str-concat-large-array
Roy Tinker

1
@ RoyTinker Roy, oh Roy, testleriniz aldatıyor çünkü diziyi testin kurulumunda oluşturuyorsunuz. İşte farklı karakterleri kullanan gerçek test jsperf.com/string-concat-without-sringbuilder/7 Yeni test senaryoları oluşturmaktan çekinmeyin, ancak dizi oluşturmak testin kendisinin bir parçasıdır
Juan Mendes

@JuanMendes Amacım, test senaryosunu joinvs dizesi birleşiminin sıkı bir karşılaştırmasıyla daraltmaktı , dolayısıyla testten önce diziyi oluşturmaktı. Bu hedef anlaşılırsa (ve joindahili olarak dizi numaralandırır, bu hile sanmıyorum , bu yüzden forde jointestten bir döngü atlamak için hile değil).
Roy Tinker

2
@RoyTinker Evet, herhangi bir dize oluşturucu dizinin oluşturulmasını gerektirecektir. Soru, bir dize oluşturucunun gerekli olup olmadığıdır. Dizede zaten dizeler varsa, o zaman burada tartıştığımız şey için geçerli bir test örneği değildir
Juan Mendes

1
@Baltusaj Index / Push yazan sütunlar kullanılıyorArray.join
Juan Mendes

41

dan gergedan kitabında :

JavaScript'te, dizeler değiştirilemez nesnelerdir, bu da onların içindeki karakterlerin değiştirilemeyebileceği ve dizelerdeki herhangi bir işlemin gerçekten yeni dizeler oluşturduğu anlamına gelir. Dizeler değere göre değil, başvuru ile atanır. Genel olarak, bir nesne referans olarak atandığında, nesneye bir referans yoluyla yapılan bir değişiklik, nesneye yapılan tüm diğer referanslar aracılığıyla görülebilir. Ancak dizeler değiştirilemediğinden, bir dize nesnesine birden çok başvurunuz olabilir ve dize değerinin siz bilmeden değişeceğinden endişe etmeyin


7
Gergedan kitabının uygun bir bölümüne bağlantı: books.google.com/…
baudtack

116
Rhino kitap alıntısı (ve dolayısıyla bu cevap) burada yanlış . JavaScript dizelerinde ilkel değer türleri vardır, nesneler değil (spec) . Aslında, ES5 itibariyle, bunlar yanında sadece 5 değer türleri birisiniz null undefined numberve boolean. Yaylı tarafından atanır değeri ve değildir referansla ve bu şekilde geçirilir. Böylece, teller sadece değişmez değil, bir değerdir . Dize değiştirme "hello"olmak "world"3 numaralı artık o karar vermenin yerini sayı 4 ... hiç mantıklı değildir.
Benjamin Gruenbaum

8
Evet, benim yorumum gibi dizeleri söyler vardır onlar ilkel değer türleridir - değişmez, ancak referans tipleri değildir ne de nesnelerdir. İkisinin de olmadığını görmek için kolay bir yol, bir dizeye bir özellik eklemek ve sonra okumak okumak olacaktır:var a = "hello";var b=a;a.x=5;console.log(a.x,b.x);
Benjamin Gruenbaum

9
@ VidarS.Ramdal Hayır, Stringdize yapıcısı kullanılarak oluşturulan nesneler JavaScript dize değerlerinin etrafındaki sarmalayıcılardır . Kutulu türün dize değerine .valueOf()işlevi kullanarak erişebilirsiniz - bu, Numbernesneler ve sayı değerleri için de geçerlidir . StringKullanarak oluşturulan nesnelerin new Stringgerçek dizeler değil, dizelerin etrafındaki sarmalayıcılar veya kutular olduğunu belirtmek önemlidir . Bkz. Es5.github.io/#x15.5.2.1 . Nesnelerin nesnelere nasıl dönüştüğü hakkında bkz. Es5.github.io/#x9.9
Benjamin

5
Neden bazı insanlar dizelerin nesne olduğunu söylüyorlarsa, muhtemelen Python veya Lisp'den ya da spekülasyonlarının herhangi bir veriyi (hatta tamsayılar) ifade etmek için "nesne" kelimesini kullandığı başka bir dilden geliyorlar. Sadece ECMA spesifikasyonunun "Object türünün üyesi" kelimesini nasıl tanımladığını okumalıdırlar. Ayrıca, "değer" kelimesi bile farklı dillerin özelliklerine göre farklı anlamlara gelebilir.
Jisang Yoo

21

Performans ipucu:

Büyük dizeleri birleştirmeniz gerekiyorsa, dize parçalarını bir diziye yerleştirin ve Array.Join()genel dizeyi almak için yöntemi kullanın. Bu, çok sayıda dizeyi birleştirmek için çok daha hızlı olabilir.

StringBuilderJavaScript'te yok .


Ben bir stringBuilder olmadığını biliyorum, msAjax bir tane var, ve ben sadece yararlı olup olmadığını düşünüyor
DevelopingChris

5
Bunun iplerin değişmez olması ya da olmaması ile ne ilgisi var?
Baudtack

4
@docgnome: Dizeler değişmez olduğundan, dize birleştirme işlemi Array.join yaklaşımından daha fazla nesne oluşturmayı gerektirir
Juan Mendes

8
Juan'ın yukarıdaki testine göre, dize birleştirmesi hem IE hem de Chrome'da aslında daha hızlı, Firefox'ta ise daha yavaş.
Bill Yang

9
Cevabınızı güncellemeyi düşünün, uzun zaman önce doğru olabilir, ancak artık geçerli değil. Bkz. Jsperf.com/string-concat-without-sringbuilder/5
Juan Mendes

8

Sadece benim gibi basit zihinleri açıklığa kavuşturmak için ( MDN'den ):

Dokunulmazlar, nesne oluşturulduktan sonra durumu değiştirilemeyen nesnelerdir.

Dize ve Sayılar Değişmezdir.

Değişmez şu anlama gelir:

Değişken adı yeni bir değere işaret edebilir, ancak önceki değer hala bellekte tutulur. Bu nedenle çöp toplama ihtiyacı.

var immutableString = "Hello";

// Yukarıdaki kodda, dize değerine sahip yeni bir nesne oluşturulur.

immutableString = immutableString + "World";

// Şimdi mevcut değere "Dünya" yı ekliyoruz.

Bu, 'immutableString' dizesini değiştiriyor gibi görünüyor, ama değiliz. Yerine:

Bir dize değeriyle "immutableString" eklenirken aşağıdaki olaylar gerçekleşir:

  1. Mevcut "immutableString" değeri alındı
  2. "İmmutableString" ifadesinin değerine "Dünya" eklenir
  3. Elde edilen değer daha sonra yeni bir bellek bloğuna tahsis edilir
  4. "immutableString" nesnesi artık yeni oluşturulan bellek alanını gösteriyor
  5. Daha önce oluşturulan bellek alanı artık çöp toplama için kullanılabilir.

4

Dize türü değeri değiştirilemez, ancak String () yapıcısı kullanılarak oluşturulan String nesnesi değiştirilebilir, çünkü bu bir nesne ve buna yeni özellikler ekleyebilirsiniz.

> var str = new String("test")
undefined
> str
[String: 'test']
> str.newProp = "some value"
'some value'
> str
{ [String: 'test'] newProp: 'some value' }

Bu arada, yeni özellikler ekleyebilmenize rağmen, mevcut özellikleri değiştiremezsiniz

Chrome konsolundaki bir testin ekran görüntüsü

Sonuç olarak, 1. tüm dize tipi değeri (ilkel tip) değişmezdir. 2. String nesnesi değiştirilebilir, ancak içerdiği dize türü değeri (ilkel tür) değiştirilemez.


Javascript String nesneleri değiştirilemez developer.mozilla.org/tr-TR/docs/Web/JavaScript/Data_structures
prasun

@prasun ancak bu sayfada şöyle diyor: "Nesneler dışındaki tüm türler değişmez değerleri tanımlar (değiştirilemeyen değerler)." String nesneleri nesnedir. Ve üzerine yeni özellikler ekleyebilirseniz nasıl değişmez?
zhanziyang

"String Type" bölümünü okuyun. Javascript'in String bağlantısı hem ilkel hem de Object'i işaret eder ve daha sonra "JavaScript dizeleri değişmez" der. Belgenin iki farklı notta çakıştığı için bu konuda net değil gibi görünüyor
Prasun

6
new Stringdeğişmez bir ipin etrafında değişebilir bir ambalaj oluşturur
tomdemuyt

1
Yukarıdaki @ zhanziyang kodunu çalıştırarak test etmek çok kolaydır. Bir Stringnesneye (sarmalayıcı) tamamen yeni özellikler ekleyebilirsiniz , yani değişmez değildir (varsayılan olarak; diğer nesneler gibi Object.freeze, onu değişmez hale getirmek için çağırabilirsiniz ). Ancak, bir Stringnesne sarıcısında olsun ya da olmasın, ilkel bir dize değeri türü her zaman değişmezdir.
Mark Reed

3

Teller değişmez - değişemezler, sadece yeni dizeler yapabiliriz.

Misal:

var str= "Immutable value"; // it is immutable

var other= statement.slice(2, 10); // new string

1

ASP.NET Ajax'ta StringBuilder hakkındaki sorunuz hakkında (Ash'in cevabına yaptığınız yorumda) uzmanlar bu konuda katılmıyor gibi görünüyor.

Christian Wenz, Programlama ASP.NET AJAX (O'Reilly) kitabında "bu yaklaşımın bellek üzerinde ölçülebilir bir etkisi olmadığını söylüyor (aslında, uygulamanın standart yaklaşımdan daha yavaş bir kene olduğu görülüyor).

Öte yandan Gallo ve arkadaşları, ASP.NET AJAX in Action (Manning) kitabında "Birleştirilecek dizelerin sayısı daha fazla olduğunda, dize oluşturucu büyük performans düşüşlerini önlemek için önemli bir nesne haline gelir."

Kendi karşılaştırmanızı yapmanız gerektiğini düşünüyorum ve sonuçlar tarayıcılar arasında da farklılık gösterebilir. Bununla birlikte, performansı artırmasa bile, C # veya Java gibi dillerde StringBuilders ile kodlamaya alışkın olan programcılar için yine de "yararlı" olarak kabul edilebilir.


1

Geç bir gönderi, ama cevaplar arasında iyi bir kitap teklifi bulamadım.

İşte güvenilir bir kitap haricinde kesin:

Dizeler ECMAScript'te değiştirilemez, yani bir kez oluşturulduklarında değerleri değişemez. Bir değişken tarafından tutulan dizgiyi değiştirmek için orijinal dizenin yok edilmesi ve değişkenin yeni bir değer içeren başka bir dizeyle doldurulması gerekir ... —Web Geliştiricileri için Profesyonel JavaScript, 3. Baskı., S.43

Şimdi, Rhino kitabının alıntılarından alıntı yapan cevap, ipin değişmezliği konusunda doğru ama "Dizeler değere göre değil referansla atanır" şeklinde yanlıştır. (muhtemelen başlangıçta kelimeleri zıt bir şekilde ifade etmek istediler).

"Referans / değer" yanılgısı, "Temel JavaScript ve Referans değerleri" adlı "Profesyonel JavaScript" bölümünde açıklanmıştır:

Beş temel tür ... [şunlardır]: Tanımsız, Boş, Boole, Sayı ve Dize. Değişkende saklanan gerçek değeri değiştirdiğiniz için bu değişkenlere değere erişildiği söylenir. —Web Geliştiricileri için Profesyonel JavaScript, 3. Baskı, s.85

nesnelerin aksine :

Bir nesneyi işlediğinizde, gerçek nesnenin kendisinden ziyade o nesneye yapılan bir başvuru üzerinde çalışırsınız. Bu nedenle, bu değerlere referans olarak erişildiği söylenir. —Web Geliştiricileri için Profesyonel JavaScript, 3. Baskı, s.85


FWIW: Rhino kitabı muhtemelen bir dize atamasının dahili olarak / uygulamada bir işaretçiyi (dizenin içeriğini kopyalamak yerine) sakladığı / kopyaladığı anlamına gelir . Ondan sonraki metne dayanarak kendi adına bir kaza gibi görünmüyor. Ama katılıyorum: "referans olarak" terimini kötüye kullanıyorlar. Sadece "referans olarak" değil sadece uygulama işaretçiler geçer (performans için). Wiki - değerlendirme stratejisi bu konuda ilginç bir okuma.
ToolmakerSteve


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.