Apa yang terjadi jika objek ditambahkan obj1 + obj2
, dikurangi obj1 - obj2
atau dicetak menggunakan alert(obj)
?
JavaScript tidak mengizinkan Anda menyesuaikan cara kerja operator pada objek. Tidak seperti beberapa bahasa pemrograman lainnya, seperti Ruby atau C++, kami tidak dapat mengimplementasikan metode objek khusus untuk menangani penambahan (atau operator lain).
Dalam kasus operasi seperti itu, objek secara otomatis dikonversi ke primitif, dan kemudian operasi dilakukan pada primitif ini dan menghasilkan nilai primitif.
Itu batasan penting: hasil dari obj1 + obj2
(atau operasi matematika lainnya) tidak boleh berupa objek lain!
Misalnya kita tidak bisa membuat objek yang mewakili vektor atau matriks (atau pencapaian atau apa pun), menjumlahkannya dan mengharapkan objek yang “dijumlahkan” sebagai hasilnya. Prestasi arsitektur seperti itu secara otomatis “diluar kebiasaan”.
Jadi, karena secara teknis kita tidak bisa berbuat banyak di sini, tidak ada matematika dengan objek dalam proyek nyata. Jika hal ini terjadi, dengan pengecualian yang jarang terjadi, hal ini disebabkan oleh kesalahan pengkodean.
Dalam bab ini kita akan membahas bagaimana suatu objek diubah menjadi primitif dan cara menyesuaikannya.
Kami memiliki dua tujuan:
Date
). Kami akan menemukannya nanti.Dalam bab Tipe Konversi kita telah melihat aturan untuk konversi numerik, string, dan boolean primitif. Tapi kami meninggalkan celah untuk objek. Sekarang, seperti yang kita ketahui tentang metode dan simbol, kita dapat mengisinya.
true
dalam konteks boolean, sesederhana itu. Yang ada hanya konversi numerik dan string.Date
(yang akan dibahas dalam bab Tanggal dan waktu) dapat dikurangi, dan hasil dari date1 - date2
adalah perbedaan waktu antara dua tanggal.alert(obj)
dan dalam konteks serupa.Kita dapat mengimplementasikan konversi string dan numerik sendiri, menggunakan metode objek khusus.
Sekarang mari masuk ke detail teknisnya, karena itulah satu-satunya cara untuk membahas topik ini secara mendalam.
Bagaimana JavaScript memutuskan konversi mana yang akan diterapkan?
Ada tiga varian konversi tipe, yang terjadi dalam berbagai situasi. Itu disebut "petunjuk", seperti yang dijelaskan dalam spesifikasi:
"string"
Untuk konversi objek ke string, saat kita melakukan operasi pada objek yang mengharapkan string, seperti alert
:
// output alert(obj); // using object as a property key anotherObj[obj] = 123;
"number"
Untuk konversi objek ke angka, seperti saat kita mengerjakan matematika:
// explicit conversion let num = Number(obj); // maths (except binary plus) let n = +obj; // unary plus let delta = date1 - date2; // less/greater comparison let greater = user1 > user2;
Sebagian besar fungsi matematika bawaan juga menyertakan konversi tersebut.
"default"
Terjadi dalam kasus yang jarang terjadi ketika operator “tidak yakin” jenis apa yang diharapkan.
Misalnya, biner plus +
dapat bekerja dengan string (menggabungkannya) dan angka (menambahkannya). Jadi, jika biner plus mendapatkan objek sebagai argumen, ia menggunakan petunjuk "default"
untuk mengonversinya.
Selain itu, jika suatu objek dibandingkan menggunakan ==
dengan string, angka, atau simbol, konversi mana yang harus dilakukan juga tidak jelas, sehingga petunjuk "default"
digunakan.
// binary plus uses the "default" hint let total = obj1 + obj2; // obj == number uses the "default" hint if (user == 1) { ... };
Operator perbandingan yang lebih besar dan lebih kecil, seperti <
>
, dapat bekerja dengan string dan angka juga. Namun, mereka menggunakan petunjuk "number"
, bukan "default"
. Itu karena alasan historis.
Namun dalam praktiknya, segalanya menjadi lebih sederhana.
Semua objek bawaan kecuali satu case ( Objek Date
, kita akan mempelajarinya nanti) menerapkan konversi "default"
dengan cara yang sama seperti "number"
. Dan kita mungkin harus melakukan hal yang sama.
Namun, penting untuk mengetahui ketiga petunjuk tersebut, kita akan segera mengetahui alasannya.
Untuk melakukan konversi, JavaScript mencoba mencari dan memanggil tiga metode objek:
obj[Symbol.toPrimitive](hint)
– metode dengan kunci simbolis Symbol.toPrimitive
(simbol sistem), jika metode tersebut ada,"string"
obj.toString()
atau obj.valueOf()
, apa pun yang ada."number"
atau "default"
obj.valueOf()
atau obj.toString()
, apa pun yang ada. Mari kita mulai dari cara pertama. Ada simbol bawaan bernama Symbol.toPrimitive
yang harus digunakan untuk memberi nama metode konversi, seperti ini:
obj[Symbol.toPrimitive] = function(hint) { // here goes the code to convert this object to a primitive // it must return a primitive value // hint = one of "string", "number", "default" };
Jika metode Symbol.toPrimitive
ada, metode tersebut digunakan untuk semua petunjuk, dan tidak diperlukan metode lagi.
Misalnya, di sini objek user
mengimplementasikannya:
let user = { name: "John", money: 1000, [Symbol.toPrimitive](hint) { alert(`hint: ${hint}`); return hint == "string" ? `{name: "${this.name}"}` : this.money; } }; // conversions demo: alert(user); // hint: string -> {name: "John"} alert(+user); // hint: number -> 1000 alert(user + 500); // hint: default -> 1500
Seperti yang dapat kita lihat dari kodenya, user
menjadi string deskriptif atau jumlah uang, bergantung pada konversinya. Metode tunggal user[Symbol.toPrimitive]
menangani semua kasus konversi.
Jika tidak ada Symbol.toPrimitive
maka JavaScript mencoba menemukan metode toString
dan valueOf
:
"string"
: panggil metode toString
, dan jika tidak ada atau jika mengembalikan objek alih-alih nilai primitif, panggil valueOf
(sehingga toString
memiliki prioritas untuk konversi string).valueOf
, dan jika tidak ada atau jika mengembalikan objek alih-alih nilai primitif, panggil toString
(sehingga valueOf
memiliki prioritas untuk matematika). Metode toString
dan valueOf
berasal dari zaman kuno. Itu bukanlah simbol (simbol belum ada sejak lama), melainkan metode bernama string “biasa”. Mereka memberikan alternatif cara “gaya lama” untuk menerapkan konversi.
Metode ini harus mengembalikan nilai primitif. Jika toString
atau valueOf
mengembalikan sebuah objek, maka objek tersebut akan diabaikan (sama seperti jika tidak ada metode).
Secara default, objek biasa memiliki metode toString
dan valueOf
berikut:
toString
mengembalikan string "[object Object]"
.valueOf
mengembalikan objek itu sendiri.Berikut demonya:
let user = {name: "John"}; alert(user); // [object Object] alert(user.valueOf() === user); // true
Jadi jika kita mencoba menggunakan suatu objek sebagai string, seperti dalam alert
atau lebih, maka secara default kita melihat [object Object]
.
valueOf
default disebutkan di sini hanya demi kelengkapan, untuk menghindari kebingungan. Seperti yang Anda lihat, ia mengembalikan objek itu sendiri, sehingga diabaikan. Jangan tanya kenapa, itu karena alasan sejarah. Jadi kita bisa berasumsi itu tidak ada.
Mari terapkan metode ini untuk menyesuaikan konversi.
Misalnya, di sini user
melakukan hal yang sama seperti di atas menggunakan kombinasi toString
dan valueOf
alih-alih Symbol.toPrimitive
:
let user = { name: "John", money: 1000, // for hint="string" toString() { return `{name: "${this.name}"}`; }, // for hint="number" or "default" valueOf() { return this.money; } }; alert(user); // toString -> {name: "John"} alert(+user); // valueOf -> 1000 alert(user + 500); // valueOf -> 1500
Seperti yang bisa kita lihat, perilakunya sama dengan contoh sebelumnya dengan Symbol.toPrimitive
.
Seringkali kita menginginkan satu tempat yang “menampung semua” untuk menangani semua konversi primitif. Dalam hal ini, kita hanya dapat mengimplementasikan toString
saja, seperti ini:
let user = { name: "John", toString() { return this.name; } }; alert(user); // toString -> John alert(user + 500); // toString -> John500
Dengan tidak adanya Symbol.toPrimitive
dan valueOf
, toString
akan menangani semua konversi primitif.
Hal penting yang perlu diketahui tentang semua metode konversi primitif adalah bahwa metode tersebut tidak serta merta mengembalikan primitif yang “diisyaratkan”.
Tidak ada kontrol apakah toString
mengembalikan string dengan tepat, atau apakah metode Symbol.toPrimitive
mengembalikan nomor untuk petunjuk "number"
.
Satu-satunya hal yang wajib: metode ini harus mengembalikan primitif, bukan objek.
Karena alasan historis, jika toString
atau valueOf
mengembalikan objek, tidak ada kesalahan, namun nilai tersebut diabaikan (seperti jika metode tidak ada). Itu karena pada zaman dahulu tidak ada konsep “kesalahan” yang baik dalam JavaScript.
Sebaliknya, Symbol.toPrimitive
lebih ketat, ia harus mengembalikan primitif, jika tidak maka akan terjadi kesalahan.
Seperti yang telah kita ketahui, banyak operator dan fungsi yang melakukan konversi tipe, misalnya perkalian *
mengubah operan menjadi angka.
Jika kita melewatkan suatu objek sebagai argumen, maka ada dua tahap perhitungan:
Misalnya:
let obj = { // toString handles all conversions in the absence of other methods toString() { return "2"; } }; alert(obj * 2); // 4, object converted to primitive "2", then multiplication made it a number
obj * 2
pertama-tama mengubah objek menjadi primitif (yaitu string "2"
)."2" * 2
menjadi 2 * 2
(string diubah menjadi angka).Biner plus akan menggabungkan string dalam situasi yang sama, karena ia dengan senang hati menerima string:
let obj = { toString() { return "2"; } }; alert(obj + 2); // "22" ("2" + 2), conversion to primitive returned a string => concatenation
Konversi objek ke primitif dipanggil secara otomatis oleh banyak fungsi dan operator bawaan yang mengharapkan nilai primitif.
Ada 3 jenis (petunjuk):
"string"
(untuk alert
dan operasi lain yang memerlukan string)"number"
(untuk matematika)"default"
(beberapa operator, biasanya objek mengimplementasikannya dengan cara yang sama seperti "number"
)Spesifikasi tersebut menjelaskan secara eksplisit operator mana yang menggunakan petunjuk mana.
Algoritma konversinya adalah:
obj[Symbol.toPrimitive](hint)
jika metodenya ada,"string"
obj.toString()
atau obj.valueOf()
, apa pun yang ada."number"
atau "default"
obj.valueOf()
atau obj.toString()
, apa pun yang ada.Semua metode ini harus mengembalikan primitif agar berfungsi (jika ditentukan).
Dalam praktiknya, seringkali cukup mengimplementasikan hanya obj.toString()
sebagai metode “catch-all” untuk konversi string yang akan mengembalikan representasi objek yang “dapat dibaca manusia”, untuk tujuan logging atau debugging.