Apakah string JavaScript tidak dapat diubah? Apakah saya memerlukan "pembuat string" dalam JavaScript?


237

Apakah javascript menggunakan string yang tidak dapat diubah atau berubah? Apakah saya memerlukan "pembuat string"?


3
Ya, y itu tidak dapat diubah dan Anda membutuhkan "pembuat string". Membaca ini blog.codeeffects.com/Article/String-Builder-In-Java-Script atau ini codeproject.com/KB/scripting/stringbuilder.aspx
KIZZ

2
Menarik, contoh-contoh itu bertentangan dengan temuan saya dalam jawaban saya.
Juan Mendes

Jawaban:


294

Mereka tidak berubah. Anda tidak dapat mengubah karakter dalam string dengan sesuatu seperti var myString = "abbdef"; myString[2] = 'c'. Metode manipulasi string seperti trim, slicemengembalikan string baru.

Dengan cara yang sama, jika Anda memiliki dua referensi ke string yang sama, memodifikasi satu tidak mempengaruhi yang lain

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

Namun, saya selalu mendengar apa yang disebutkan Ash dalam jawabannya (bahwa menggunakan Array.join lebih cepat untuk penggabungan) jadi saya ingin menguji berbagai metode merangkai string dan mengabstraksi cara tercepat menjadi StringBuilder. Saya menulis beberapa tes untuk melihat apakah ini benar (tidak!).

Ini adalah apa yang saya yakini sebagai cara tercepat, meskipun saya terus berpikir bahwa menambahkan pemanggilan metode mungkin membuatnya lebih lambat ...

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

Berikut ini adalah tes kecepatan kinerja. Mereka bertiga menciptakan string raksasa yang terdiri dari penggabungan"Hello diggity dog" seratus ribu kali menjadi string kosong.

Saya telah membuat tiga jenis tes

  • Menggunakan Array.pushdanArray.join
  • Menggunakan pengindeksan Array untuk menghindari Array.push, lalu menggunakanArray.join
  • Rangkaian string lurus

Kemudian saya membuat tiga tes yang sama dengan mengabstraksikannya ke dalam StringBuilderConcat, StringBuilderArrayPushdan StringBuilderArrayIndex http://jsperf.com/string-concat-without-sringbuilder/5 Silakan pergi ke sana dan menjalankan tes sehingga kita bisa mendapatkan sampel yang bagus. Perhatikan bahwa saya memperbaiki bug kecil, sehingga data untuk tes dihapus, saya akan memperbarui tabel setelah ada cukup data kinerja. Pergi ke http://jsperf.com/string-concat-without-sringbuilder/5 untuk tabel data lama.

Berikut adalah beberapa angka (Pembaruan terbaru di Ma5rch 2018), jika Anda tidak ingin mengikuti tautan. Angka pada setiap tes dalam 1000 operasi / detik ( lebih tinggi lebih baik )

| 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     | 

Temuan

  • Saat ini, semua browser evergreen menangani penggabungan string dengan baik. Array.joinhanya membantu IE 11

  • Secara keseluruhan, Opera tercepat, 4 kali lebih cepat dari Array.join

  • Firefox adalah yang kedua dan Array.joinhanya sedikit lebih lambat di FF tetapi jauh lebih lambat (3x) di Chrome.

  • Chrome adalah yang ketiga tetapi string concat 3 kali lebih cepat dari Array.join

  • Membuat StringBuilder tampaknya tidak terlalu mempengaruhi kinerja.

Semoga orang lain menemukan ini berguna

Kasus Uji yang berbeda

Karena @RoyTinker berpikir bahwa pengujian saya salah, saya membuat case baru yang tidak membuat string besar dengan menggabungkan string yang sama, menggunakan karakter yang berbeda untuk setiap iterasi. Rangkaian senar masih tampak lebih cepat atau sama cepatnya. Mari kita menjalankan tes-tes itu.

Saya menyarankan semua orang harus terus memikirkan cara lain untuk menguji ini, dan merasa bebas untuk menambahkan tautan baru ke berbagai kasus uji di bawah ini.

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


@Juan, tautan yang Anda minta kami kunjungi menyatukan string 112-char 30 kali. Berikut ini tes lain yang dapat membantu menyeimbangkan hal-hal - Array.join vs string concatenation pada 20.000 string 1-char yang berbeda (bergabung jauh lebih cepat di IE / FF). jsperf.com/join-vs-str-concat-large-array
Roy Tinker

1
@ RoyTinker Roy, oh Roy, tes Anda curang karena Anda membuat array dalam pengaturan tes. Inilah tes sesungguhnya menggunakan karakter yang berbeda jsperf.com/string-concat-without-sringbuilder/7 Jangan ragu untuk membuat case uji baru, tetapi membuat array adalah bagian dari tes itu sendiri
Juan Mendes

@JuanMendes Tujuan saya adalah untuk mempersempit test case menjadi perbandingan yang ketat antara joinconcatenation vs string, karenanya membangun array sebelum pengujian. Saya tidak berpikir itu curang jika tujuan itu dipahami (dan joinmenyebutkan array secara internal, jadi tidak curang untuk menghilangkan forloop dari jointes, baik).
Roy Tinker

2
@RoyTinker Ya, pembuat string apa pun akan membutuhkan pembuatan array. Pertanyaannya adalah apakah pembangun string diperlukan. Jika Anda sudah memiliki string dalam array, maka itu bukan kasus uji yang valid untuk apa yang kita diskusikan di sini
Juan Mendes

1
@Baltusaj Kolom yang bertuliskan Indeks / Dorong gunakanArray.join
Juan Mendes

41

dari buku badak :

Dalam JavaScript, string adalah objek yang tidak dapat diubah, yang berarti bahwa karakter di dalamnya tidak boleh diubah dan bahwa operasi apa pun pada string benar-benar membuat string baru. String diberikan oleh referensi, bukan oleh nilai. Secara umum, ketika suatu objek ditugaskan oleh referensi, perubahan yang dilakukan pada objek melalui satu referensi akan terlihat melalui semua referensi lain ke objek. Karena string tidak dapat diubah, Anda dapat memiliki banyak referensi ke objek string dan tidak khawatir bahwa nilai string akan berubah tanpa Anda sadari


7
Tautan ke bagian buku badak yang sesuai: books.google.com/...
baudtack

116
Kutipan buku Badak (dan dengan demikian jawaban ini) salah di sini. Dalam JavaScript string adalah tipe nilai primitif dan bukan objek (spek) . Bahkan, pada ES5, mereka adalah satu dari hanya 5 tipe nilai di samping null undefined numberdan boolean. String diberikan oleh nilai dan bukan oleh referensi dan diteruskan demikian. Jadi, string bukan hanya tidak berubah, mereka adalah sebuah nilai . Mengubah string "hello"menjadi "world"seperti memutuskan bahwa mulai sekarang pada angka 3 adalah angka 4 ... itu tidak masuk akal.
Benjamin Gruenbaum

8
Ya, seperti komentar saya, string tidak dapat diubah, tetapi mereka bukan tipe referensi atau objek - mereka adalah tipe nilai primitif. Cara mudah untuk melihat mereka tidak akan mencoba menambahkan properti ke string dan kemudian membacanya:var a = "hello";var b=a;a.x=5;console.log(a.x,b.x);
Benjamin Gruenbaum

9
@ VidarS.Ramdal Tidak, Stringobjek yang dibuat menggunakan konstruktor string adalah pembungkus di sekitar nilai string JavaScript. Anda dapat mengakses nilai string dari tipe kotak menggunakan .valueOf()fungsi - ini juga berlaku untuk Numberobjek dan nilai angka. Penting untuk mencatat Stringobjek yang dibuat menggunakan new Stringbukan string aktual tetapi pembungkus atau kotak di sekitar string. Lihat es5.github.io/#x15.5.2.1 . Tentang bagaimana benda dikonversi menjadi objek, lihat es5.github.io/#x9.9
Benjamin Gruenbaum

5
Adapun mengapa beberapa orang mengatakan string adalah objek, mereka mungkin berasal dari Python atau Lisp atau bahasa lain di mana specnya menggunakan kata "objek" untuk berarti segala jenis datum (bahkan bilangan bulat). Mereka hanya perlu membaca bagaimana spec ECMA mendefinisikan kata: "member of type Object". Juga, bahkan kata "nilai" dapat berarti hal yang berbeda sesuai dengan spesifikasi bahasa yang berbeda.
Jisang Yoo

21

Kiat kinerja:

Jika Anda harus menggabungkan string besar, masukkan bagian string ke dalam array dan gunakan Array.Join()metode untuk mendapatkan keseluruhan string. Ini bisa berkali-kali lebih cepat untuk menggabungkan sejumlah besar string.

Tidak ada StringBuilderdalam JavaScript.


Saya tahu tidak ada stringBuilder, msAjax memilikinya, dan saya hanya memikirkan apakah ini berguna atau tidak
DevelopingChris

5
Apa hubungannya ini dengan string yang tidak berubah atau tidak?
baudtack

4
@docgnome: Karena string tidak dapat diubah, rangkaian string membutuhkan pembuatan lebih banyak objek daripada pendekatan Array.join
Juan Mendes

8
Menurut tes Juan di atas, rangkaian string sebenarnya lebih cepat di IE dan Chrome, sementara lebih lambat di Firefox.
Bill Yang

9
Pertimbangkan untuk memperbarui jawaban Anda, itu mungkin benar dulu, tetapi sekarang tidak lagi. Lihat jsperf.com/string-concat-without-sringbuilder/5
Juan Mendes

8

Hanya untuk memperjelas pikiran sederhana seperti milik saya (dari MDN ):

Immutables adalah objek yang kondisinya tidak dapat diubah setelah objek dibuat.

String dan Angka Tidak Berubah.

Abadi berarti bahwa:

Anda bisa membuat nama variabel menunjuk ke nilai baru, tetapi nilai sebelumnya masih tersimpan di memori. Karenanya perlu pengumpulan sampah.

var immutableString = "Hello";

// Dalam kode di atas, objek baru dengan nilai string dibuat.

immutableString = immutableString + "World";

// Kami sekarang menambahkan "Dunia" ke nilai yang ada.

Ini terlihat seperti kita memutasikan string 'immutableString', tapi ternyata tidak. Sebagai gantinya:

Pada menambahkan "immutableString" dengan nilai string, peristiwa berikut terjadi:

  1. Nilai "immutableString" yang ada diambil
  2. "World" ditambahkan ke nilai "immutableString" yang ada
  3. Nilai yang dihasilkan kemudian dialokasikan ke blok memori baru
  4. Objek "immutableString" sekarang menunjuk ke ruang memori yang baru dibuat
  5. Ruang memori yang sebelumnya dibuat sekarang tersedia untuk pengumpulan sampah.

4

Nilai tipe string tidak dapat diubah, tetapi objek String, yang dibuat dengan menggunakan konstruktor String (), dapat berubah, karena ini adalah objek dan Anda dapat menambahkan properti baru ke dalamnya.

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

Sementara itu, meskipun Anda bisa menambahkan properti baru, Anda tidak bisa mengubah properti yang sudah ada

Cuplikan layar tes di konsol Chrome

Kesimpulannya, 1. semua nilai tipe string (tipe primitif) tidak dapat diubah. 2. Objek String dapat berubah, tetapi nilai tipe string (tipe primitif) yang dikandungnya tidak dapat diubah.



@prasun tetapi di halaman itu tertulis: "Semua tipe kecuali objek menentukan nilai yang tidak dapat diubah (nilai, yang tidak dapat diubah)." Objek string adalah objek. Dan bagaimana itu tidak berubah jika Anda dapat menambahkan properti baru di atasnya?
zhanziyang

baca bagian "Tipe String". Tautan String Javascript mengarah ke primitif dan Obyek dan kemudian dikatakan "string JavaScript tidak dapat diubah". Sepertinya dokumen ini tidak jelas mengenai topik ini karena bertentangan dengan dua catatan berbeda
prasun

6
new Stringmenghasilkan wrapper yang bisa berubah-ubah di sekitar string yang tidak dapat diubah
tomdemuyt

1
Sangat mudah untuk menguji dengan menjalankan kode @ zhanziyang di atas. Anda benar-benar dapat menambahkan properti baru ke Stringobjek (pembungkus), yang berarti itu tidak dapat diubah (secara default; seperti objek lain yang dapat Anda panggil Object.freezeuntuk menjadikannya tidak berubah). Tetapi tipe nilai string primitif, apakah terkandung dalam Stringobjek wrapper atau tidak, selalu tidak berubah.
Mark Reed

3

String tidak dapat diubah - mereka tidak dapat berubah, kita hanya bisa membuat string baru.

Contoh:

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

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

1

Mengenai pertanyaan Anda (dalam komentar Anda untuk tanggapan Ash) tentang StringBuilder di ASP.NET Ajax, para ahli tampaknya tidak setuju dengan yang satu ini.

Christian Wenz mengatakan dalam bukunya Programming ASP.NET AJAX (O'Reilly) bahwa "pendekatan ini tidak memiliki efek yang terukur pada memori (pada kenyataannya, implementasi tampaknya lebih cepat daripada pendekatan standar)."

Di sisi lain Gallo dkk mengatakan dalam buku mereka ASP.NET AJAX in Action (Manning) bahwa "Ketika jumlah string untuk digabungkan lebih besar, pembuat string menjadi objek penting untuk menghindari penurunan kinerja besar."

Saya kira Anda perlu melakukan pembandingan sendiri dan hasilnya mungkin berbeda di antara peramban juga. Namun, bahkan jika itu tidak meningkatkan kinerja, itu mungkin masih dianggap "berguna" untuk programmer yang digunakan untuk pengkodean dengan StringBuilders dalam bahasa seperti C # atau Java.


1

Ini posting yang terlambat, tetapi saya tidak menemukan kutipan buku yang bagus di antara jawaban.

Inilah yang pasti kecuali dari buku yang dapat diandalkan:

String tidak dapat diubah dalam ECMAScript, artinya setelah dibuat, nilainya tidak dapat berubah. Untuk mengubah string yang dipegang oleh suatu variabel, string asli harus dihancurkan dan variabel diisi dengan string lain yang berisi nilai baru ... —Profesional JavaScript untuk Pengembang Web, 3rd Ed., Hal.43

Sekarang, jawaban yang mengutip kutipan buku Badak itu benar tentang kekekalan string tetapi salah mengatakan "String diberikan oleh referensi, bukan oleh nilai." (mungkin mereka awalnya bermaksud menempatkan kata-kata dengan cara yang berlawanan).

Kesalahpahaman "referensi / nilai" diklarifikasi dalam "JavaScript Profesional", bab bernama "Nilai-nilai Primitif dan Referensi":

Lima tipe primitif ... adalah: Tidak terdefinisi, Null, Boolean, Number, dan String. Variabel-variabel ini dikatakan diakses oleh nilai, karena Anda memanipulasi nilai aktual yang disimpan dalam variabel. —Profesional JavaScript untuk Pengembang Web, 3rd Ed., Hal.85

yang bertentangan dengan objek :

Saat Anda memanipulasi suatu objek, Anda benar-benar bekerja pada referensi ke objek itu daripada objek itu sendiri. Untuk alasan ini, nilai-nilai tersebut dikatakan diakses dengan referensi. —Profesional JavaScript untuk Pengembang Web, 3rd Ed., Hal.85


FWIW: Buku Badak mungkin berarti bahwa secara internal / implementasi tugas string adalah menyimpan / menyalin pointer (daripada menyalin isi string). Itu tidak terlihat seperti kecelakaan di pihak mereka, berdasarkan pada teks setelah itu. Tapi saya setuju: mereka menyalahgunakan istilah "dengan referensi". Ini bukan "dengan referensi" hanya karena implementasi melewati pointer (untuk kinerja). Wiki - strategi evaluasi adalah bacaan yang menarik tentang topik ini.
ToolmakerSteve


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.