Apa perbedaan antara string primitif dan objek String di JavaScript?

116

Diambil dari MDN

String literal (dilambangkan dengan tanda kutip ganda atau tunggal) dan string yang dikembalikan dari panggilan String dalam konteks non-konstruktor (yaitu, tanpa menggunakan kata kunci baru) adalah string primitif. JavaScript secara otomatis mengonversi objek primitif menjadi String, sehingga memungkinkan untuk menggunakan metode objek String untuk string primitif. Dalam konteks di mana metode akan dipanggil pada string primitif atau terjadi pencarian properti, JavaScript akan secara otomatis menggabungkan string primitif dan memanggil metode atau melakukan pencarian properti.

Jadi, saya pikir (secara logis) operasi (panggilan metode) pada primitif string harus lebih lambat daripada operasi pada Objek string karena string primitif apa pun diubah ke Objek string (kerja ekstra) sebelum methodditerapkan pada string.

Namun dalam kasus uji ini , hasilnya justru sebaliknya. The blok kode-1 berjalan lebih cepat dari kode blok-2 , kedua blok kode yang diberikan di bawah:

blok kode-1:

var s = '0123456789';
for (var i = 0; i < s.length; i++) {
  s.charAt(i);
}

blok kode-2:

var s = new String('0123456789');
for (var i = 0; i < s.length; i++) {
    s.charAt(i);
}

Hasil bervariasi di browser tetapi blok kode-1 selalu lebih cepat. Adakah yang bisa menjelaskan ini, mengapa kode blok-1 lebih cepat daripada kode blok-2 .

Alfa
sumber
6
Menggunakan new Stringmemperkenalkan lapisan transparan lain dari pembungkus Objek . typeof new String(); //"object"
Paul S.
tentang apa '0123456789'.charAt(i)?
Yuriy Galanter
@YuriyGalanter, itu bukan masalah tapi saya bertanya mengapa code block-1lebih cepat?
Alpha
2
Objek string relatif jarang terlihat dalam konteks kehidupan nyata, oleh karena itu tidak mengherankan bagi interpreter untuk mengoptimalkan literal string. Saat ini, kode Anda tidak hanya diinterpretasikan , ada banyak lapisan pengoptimalan yang terjadi di balik layar.
Fabrício Matté
2
Ini aneh: revisi 2
hjpotter92

Jawaban:

149

JavaScript memiliki dua kategori tipe utama, primitif dan objek.

var s = 'test';
var ss = new String('test');

Pola petik tunggal / petik ganda identik dalam hal fungsionalitas. Selain itu, perilaku yang coba Anda namai disebut auto-boxing. Jadi yang sebenarnya terjadi adalah primitif diubah menjadi tipe pembungkusnya ketika metode jenis pembungkus dipanggil. Sederhanakan:

var s = 'test';

Merupakan tipe data primitif. Ini tidak memiliki metode, tidak lebih dari sebuah penunjuk ke referensi memori data mentah, yang menjelaskan kecepatan akses acak yang jauh lebih cepat.

Jadi apa yang terjadi jika Anda melakukannya s.charAt(i)misalnya?

Karena sbukan merupakan contoh dari String, JavaScript akan auto-box s, yang memiliki typeof stringjenis pembungkusnya String, dengan typeof objectatau lebih tepatnya s.valueOf(s).prototype.toString.call = [object String].

Perilaku auto-boxing sbolak-balik ke jenis pembungkusnya sesuai kebutuhan, tetapi operasi standar sangat cepat karena Anda berurusan dengan tipe data yang lebih sederhana. Bagaimanapun auto-boxing dan Object.prototype.valueOfmemiliki efek berbeda.

Jika Anda ingin memaksa auto-boxing atau mentransmisikan primitif ke jenis pembungkusnya, Anda dapat menggunakan Object.prototype.valueOf, tetapi perilakunya berbeda. Berdasarkan berbagai macam skenario pengujian, auto-boxing hanya menerapkan metode yang 'diperlukan', tanpa mengubah sifat primitif variabel. Itulah mengapa Anda mendapatkan kecepatan yang lebih baik.

flavia
sumber
33

Ini agak tergantung pada implementasi, tetapi saya akan mengambil bidikan. Saya akan mencontohkan dengan V8 tetapi saya berasumsi mesin lain menggunakan pendekatan serupa.

String primitif diurai menjadi v8::Stringobjek. Karenanya, metode dapat dipanggil langsung seperti yang disebutkan oleh jfriend00 .

Sebuah objek String, di sisi lain, diurai menjadi v8::StringObjectyang meluas Objectdan, selain menjadi objek penuh, berfungsi sebagai pembungkus untuk v8::String.

Sekarang hanya logis, panggilan untuk new String('').method()memiliki unbox ini v8::StringObject's v8::Stringsebelum mengeksekusi metode, karena itu lebih lambat.


Dalam banyak bahasa lain, nilai primitif tidak memiliki metode.

Cara MDN menempatkannya tampaknya menjadi cara paling sederhana untuk menjelaskan cara kerja auto-boxing primitif (seperti juga disebutkan dalam jawaban flav ), yaitu, bagaimana nilai primitif-y JavaScript dapat memanggil metode.

Namun, mesin pintar tidak akan mengonversi string primitive-y menjadi objek String setiap kali Anda perlu memanggil metode. Ini juga secara informatif disebutkan dalam spesifikasi ES5 Beranotasi. berkenaan dengan menyelesaikan properti (dan "metode" ¹) dari nilai primitif:

CATATAN Objek yang dapat dibuat pada langkah 1 tidak dapat diakses di luar metode di atas. Implementasi mungkin memilih untuk menghindari pembuatan objek yang sebenarnya. [...]

Pada level yang sangat rendah, String paling sering diimplementasikan sebagai nilai skalar yang tidak dapat diubah. Contoh struktur pembungkus:

StringObject > String (> ...) > char[]

Semakin jauh Anda dari primitif, semakin lama waktu yang dibutuhkan untuk mencapainya. Dalam praktiknya, Stringprimitif jauh lebih sering daripada StringObjects, oleh karena itu tidak mengherankan bagi mesin untuk menambahkan metode ke Kelas objek 'yang sesuai (ditafsirkan)' primitif String alih-alih mengonversi bolak-balik antara Stringdan StringObjectseperti yang disarankan oleh penjelasan MDN.


¹ Dalam JavaScript, "metode" hanyalah konvensi penamaan untuk properti yang menetapkan nilai fungsi tipe.

Fabrício Matté
sumber
1
Sama-sama. =]Sekarang saya bertanya-tanya apakah penjelasan MDN ada di sana hanya karena tampaknya itu cara termudah untuk memahami auto-boxing atau apakah ada referensi ke sana dalam spesifikasi ES .. Membaca seluruh spesifikasi saat ini untuk memeriksa, akan ingat untuk perbarui jawabannya jika saya pernah menemukan referensi.
Fabrício Matté
Wawasan luar biasa tentang implementasi V8. Saya akan menambahkan bahwa tinju tidak hanya untuk menyelesaikan fungsinya. Itu juga ada untuk meneruskan referensi ini ke dalam metode. Sekarang saya tidak yakin apakah V8 melewatkan ini untuk metode bawaan tetapi jika Anda menambahkan ekstensi Anda sendiri untuk mengatakan String.prototype Anda akan mendapatkan versi kotak dari objek string setiap kali dipanggil.
Ben
17

Dalam kasus string literal, kami tidak dapat menetapkan properti

var x = "hello" ;
x.y = "world";
console.log(x.y); // this will print undefined

Sedangkan dalam kasus Objek String kita dapat menetapkan properti

var x = new String("hello");
x.y = "world";
console.log(x.y); // this will print world
refactor
sumber
1
Akhirnya seseorang memotivasi mengapa kita membutuhkan Stringobjek juga. Terima kasih!
Ciprian Tomoiagă
1
Mengapa ada orang yang perlu melakukan ini?
Aditya
11

String Literal:

Literal string tidak dapat diubah, yang berarti, setelah dibuat, statusnya tidak dapat diubah, yang juga membuatnya aman untuk thread.

var a = 's';
var b = 's';

a==b Hasilnya akan menjadi 'true' kedua string merujuk objek yang sama.

Objek String:

Di sini, dua objek berbeda dibuat, dan mereka memiliki referensi berbeda:

var a = new String("s");
var b = new String("s");

a==b hasilnya akan salah, karena memiliki referensi yang berbeda.

Wajahat Ali Qureshi
sumber
1
Apakah objek string juga tidak bisa diubah?
Yang Wang
@YangWang itu bahasa konyol, untuk kedua a& bmencoba untuk menetapkan a[0] = 'X'itu akan dieksekusi berhasil tetapi akan tidak bekerja seperti yang Anda harapkan
Rux
Anda menulis "var a = 's'; var b = 's'; a == b hasilnya akan menjadi 'true' kedua string merujuk objek yang sama." Itu tidak benar: a dan b tidak mengacu pada objek yang sama, hasilnya benar karena nilainya sama. Nilai-nilai tersebut disimpan di lokasi memori yang berbeda, itulah mengapa jika Anda mengubah satu sama lain tidak berubah!
SC1000
9

Jika Anda menggunakan new, Anda secara eksplisit menyatakan bahwa Anda ingin membuat sebuah instance dari sebuah Object . Oleh karena itu, new Stringmenghasilkan sebuah Objek yang membungkus primitif String , yang berarti tindakan apa pun di atasnya melibatkan lapisan kerja tambahan.

typeof new String(); // "object"
typeof '';           // "string"

Karena memiliki tipe yang berbeda, penerjemah JavaScript Anda juga dapat mengoptimalkannya secara berbeda, seperti yang disebutkan dalam komentar .

Paul S.
sumber
5

Saat Anda menyatakan:

var s = '0123456789';

Anda membuat string primitif. String primitif itu memiliki metode yang memungkinkan Anda memanggil metode di atasnya tanpa mengonversi primitif ke objek kelas pertama. Jadi anggapan Anda bahwa ini akan lebih lambat karena string harus diubah menjadi objek tidak benar. Itu tidak harus diubah menjadi sebuah objek. Primitif itu sendiri dapat memanggil metode.

Mengonversinya menjadi objek full-blown (yang memungkinkan Anda menambahkan properti baru padanya) adalah langkah tambahan dan tidak membuat string bergerak lebih cepat (sebenarnya pengujian Anda menunjukkan bahwa hal itu membuatnya lebih lambat).

jfriend00
sumber
Mengapa string primitif mewarisi semua properti prototipe termasuk yang khusus String.prototype?
Yuriy Galanter
1
var s = '0123456789';adalah nilai primitif, bagaimana nilai ini memiliki metode, saya bingung!
Alpha
2
@SheikhHeera - bahasa primitif dibangun ke dalam implementasi bahasa sehingga penerjemah dapat memberi mereka kekuatan khusus.
jfriend00
1
@SheikhHeera - Saya tidak mengerti komentar / pertanyaan terakhir Anda. String primitif dengan sendirinya tidak mendukung Anda menambahkan properti Anda sendiri ke dalamnya. Untuk mengizinkannya, javascript juga memiliki objek String yang memiliki semua metode yang sama seperti string primitif, tetapi merupakan objek lengkap yang dapat Anda perlakukan seperti objek dengan segala cara. Bentuk ganda ini tampaknya agak berantakan, tetapi saya menduga itu dilakukan sebagai kompromi kinerja karena kasus 99% adalah penggunaan primitif dan mereka mungkin bisa lebih cepat dan lebih hemat memori daripada objek string.
jfriend00
1
@SheikhHeera "dikonversi ke string Objek" adalah cara MDN mengungkapkannya untuk menjelaskan cara primitif dapat menggunakan metode. Mereka tidak secara harfiah diubah menjadi Objek string.
Fabrício Matté
4

Saya dapat melihat bahwa pertanyaan ini telah diselesaikan sejak lama, ada perbedaan halus lainnya antara string literal dan objek string, karena tampaknya tidak ada yang menyentuhnya, saya pikir saya hanya akan menulisnya untuk kelengkapan.

Pada dasarnya perbedaan lain antara keduanya adalah saat menggunakan eval. eval ('1 + 1') memberikan 2, sedangkan eval (new String ('1 + 1')) memberikan '1 + 1', jadi jika blok kode tertentu dapat dieksekusi baik 'normal' atau dengan eval, itu bisa mengarah pada hasil yang aneh

bernuansa
sumber
Terima kasih atas masukan Anda :-)
The Alpha
Wow, itu perilaku yang sangat aneh. Anda harus menambahkan demo sebaris kecil di komentar Anda untuk menunjukkan perilaku ini - ini sangat membuka mata.
EyuelDK
ini normal, jika Anda memikirkannya. new String("")mengembalikan objek, dan mengevaluasi hanya mengevaluasi string, dan mengembalikan segala sesuatu yang lain apa adanya
Félix Brunet
3

Keberadaan objek tidak ada hubungannya dengan perilaku sebenarnya dari String di mesin ECMAScript / JavaScript karena cakupan root hanya akan berisi objek fungsi untuk ini. Jadi fungsi charAt (int) dalam kasus string literal akan dicari dan dijalankan.

Dengan objek nyata Anda menambahkan satu lapisan lagi di mana metode charAt (int) juga dicari pada objek itu sendiri sebelum perilaku standar dijalankan (sama seperti di atas). Rupanya ada banyak sekali pekerjaan yang dilakukan dalam kasus ini.

BTW Saya tidak berpikir bahwa primitif sebenarnya diubah menjadi Objek tetapi mesin skrip hanya akan menandai variabel ini sebagai tipe string dan oleh karena itu dapat menemukan semua fungsi yang disediakan untuk itu sehingga sepertinya Anda memanggil objek. Jangan lupa ini adalah runtime skrip yang bekerja pada prinsip yang berbeda dari runtime OO.

Air jernih
sumber
3

Perbedaan terbesar antara string primitif dan objek string adalah objek harus mengikuti aturan ini untuk ==operator :

Ekspresi yang membandingkan Objek hanya benar jika operan mereferensikan Objek yang sama.

Jadi, sementara string primitif memiliki kemudahan ==yang membandingkan nilai, Anda kurang beruntung ketika harus membuat jenis objek tetap lainnya (termasuk objek string) berperilaku seperti tipe nilai.

"hello" == "hello"
-> true
new String("hello") == new String("hello") // beware!
-> false

(Orang lain telah mencatat bahwa objek string secara teknis dapat berubah karena Anda dapat menambahkan properti padanya. Tetapi tidak jelas untuk apa itu berguna; nilai string itu sendiri tidak dapat berubah.)

personal_cloud
sumber
Terima kasih telah menambahkan nilai pada pertanyaan tersebut setelah waktu yang cukup lama :-)
The Alpha
1

Kode tersebut dioptimalkan sebelum dijalankan oleh mesin javascript. Secara umum, tolok ukur mikro dapat menyesatkan karena compiler dan interpreter mengatur ulang, memodifikasi, menghapus, dan melakukan trik lain pada bagian kode Anda untuk membuatnya berjalan lebih cepat. Dengan kata lain, kode tertulis memberi tahu apa tujuan tetapi compiler dan / atau runtime akan memutuskan bagaimana mencapai tujuan itu.

Blok 1 lebih cepat terutama karena: var s = '0123456789'; selalu lebih cepat dari var s = new String ('0123456789'); karena overhead pembuatan objek.

Bagian loop bukanlah yang menyebabkan perlambatan karena chartAt () bisa dibuat sebaris oleh interpreter. Coba lepaskan loop dan ulangi tes, Anda akan melihat rasio kecepatan akan sama seperti jika loop tidak dilepas. Dengan kata lain, untuk pengujian ini, blok loop pada waktu eksekusi memiliki kode bytecode / mesin yang persis sama.

Untuk jenis tolok ukur mikro ini, melihat bytecode atau kode mesin akan memberikan gambaran yang lebih jelas.

Busur
sumber
1
Terima kasih atas jawaban anda.
Alpha
0

Dalam Javascript, tipe data primitif seperti string adalah blok penyusun non-komposit. Ini berarti bahwa mereka hanyalah nilai, tidak lebih: let a = "string value"; Secara default tidak ada metode bawaan seperti toUpperCase, toLowerCase dll ...

Tetapi, jika Anda mencoba menulis:

console.log( a.toUpperCase() ); or console.log( a.toLowerCase() );

Ini tidak akan menimbulkan kesalahan apa pun, melainkan akan berfungsi sebagaimana mestinya.

Apa yang terjadi ? Nah, ketika Anda mencoba mengakses properti string aJavascript memaksa string ke objek yang new String(a);dikenal sebagai objek pembungkus .

Proses ini terkait dengan konsep yang disebut konstruktor fungsi di Javascript, di mana fungsi digunakan untuk membuat objek baru.

Ketika Anda mengetik di new String('String value');sini String adalah konstruktor fungsi, yang mengambil argumen dan membuat objek kosong di dalam ruang lingkup fungsi, objek kosong ini ditugaskan ke ini dan dalam kasus ini, String memasok semua fungsi bawaan yang diketahui yang kami sebutkan sebelumnya. dan segera setelah operasi selesai, misalnya melakukan operasi huruf besar, objek pembungkus akan dibuang.

Untuk membuktikannya, mari lakukan ini:

let justString = 'Hello From String Value';
justString.addNewProperty = 'Added New Property';
console.log( justString );

Di sini keluaran tidak akan ditentukan. Kenapa? Dalam hal ini Javascript membuat objek String pembungkus, menyetel properti addNewProperty baru dan membuang objek pembungkus segera. inilah mengapa Anda menjadi tidak terdefinisi. Kode semu akan terlihat seperti ini:

let justString = 'Hello From String Value';
let wrapperObject = new String( justString );
wrapperObject.addNewProperty = 'Added New Property'; //Do operation and discard
Alexander Gharibashvili
sumber
0

kita bisa mendefinisikan String dengan 3 cara

  1. var a = "cara pertama";
  2. var b = String ("cara kedua");
  3. var c = String baru ("cara ketiga");

// juga kita bisa membuat menggunakan 4. var d = a + '';

Periksa jenis string yang dibuat menggunakan operator typeof

  • ketik // "string"
  • tipe b // "string"
  • tipe c // "objek"


ketika Anda membandingkan a dan b var a==b ( // yes)


ketika Anda membandingkan objek String

var StringObj = new String("third way")
var StringObj2 = new String("third way")
StringObj  == StringObj2 // no result will be false, because they have different references
SouMitya chauhan
sumber