Javascript değişmez veya değişmez dizeler kullanıyor mu? Bir "string builder" lazım mı?
Javascript değişmez veya değişmez dizeler kullanıyor mu? Bir "string builder" lazım mı?
Yanıtlar:
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
, slice
yeni 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
Array.push
veArray.join
Array.push
, ardındanArray.join
Sonra onları soyutlayarak aynı üç test yarattı StringBuilderConcat
, StringBuilderArrayPush
ve 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.join
sadece 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.join
FF'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.
join
vs 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 join
dahili olarak dizi numaralandırır, bu hile sanmıyorum , bu yüzden for
de join
testten bir döngü atlamak için hile değil).
Array.join
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
null
undefined
number
ve 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.
var a = "hello";var b=a;a.x=5;console.log(a.x,b.x);
String
dize 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, Number
nesneler ve sayı değerleri için de geçerlidir . String
Kullanarak oluşturulan nesnelerin new String
gerç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
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.
StringBuilder
JavaScript'te yok .
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:
- Mevcut "immutableString" değeri alındı
- "İmmutableString" ifadesinin değerine "Dünya" eklenir
- Elde edilen değer daha sonra yeni bir bellek bloğuna tahsis edilir
- "immutableString" nesnesi artık yeni oluşturulan bellek alanını gösteriyor
- Daha önce oluşturulan bellek alanı artık çöp toplama için kullanılabilir.
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.
new String
değişmez bir ipin etrafında değişebilir bir ambalaj oluşturur
String
nesneye (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 String
nesne sarıcısında olsun ya da olmasın, ilkel bir dize değeri türü her zaman değişmezdir.
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.
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
JavaScript dizeleri gerçekten değişmezdir.
Javascript'teki dizeler değişmez