1 September 2021

String

Di Javascript, data teks disimpan sebagai string. Tidak ada tipe data sendiri untuk satu buah karakter.

UTF-16 selalu digunakan sebagai format internal string, hal tersebut tidak terikat dengan jenis encoding yang digunakan oleh halaman.

Petik

Mari kita lihat berbagai jenis petik.

String dapat ditutup dengan petik satu, petik dua, maupun backtick:

let single = 'single-quoted';
let double = "double-quoted";

let backticks = `backticks`;

Petik satu dan petik dua kurang lebih hampir sama. Akan tetapi backtick memiliki perbedaan, yaitu memperbolehkan kita untuk menyisipkan ekspresi ke dalam string, dengan menaruhnya di dalam ${???}:

function sum(a, b) {
  return a + b;
}

alert(`1 + 2 = ${sum(1, 2)}.`); // 1 + 2 = 3.

Kelebihan backtick yang lain yaitu backtick memperbolehkan sebuah string untuk terdiri lebih dari satu baris:

let guestList = `Guests:
 * John
 * Pete
 * Mary
`;

alert(guestList); // list tamu, dipisahkan per baris

Lebih rapi, kan? Tetapi petik satu atau dua tidak bekerja seperti itu.

Jika kita coba untuk menggunakan mereka untuk lebih dari satu baris, akan terjadi error:

let guestList = "Guests: // Error: Unexpected token ILLEGAL
  * John";

Petik satu dan petik dua berasal dari masa lalu saat bahasa pemrograman dibuat, dimana kebutuhan untuk string lebih dari satu baris belum dipikirkan. Backtick muncul di kemudian hari, dan lebih fleksibel.

Backtick juga memperbolehkan kita untuk menspesifikasi ???fungsi template??? sebelum backtick pertama. Syntaxnya yaitu: func`string`. Fungsi func dipanggil secara otomatis, menerima string dan ekspresi yang berada di dalamnya dan bisa memproses mereka. Ini disebut ???tagged templates???. Fitur ini membuat implementasi kustom templating lebih mudah, tapi jaran dipakai dalam praktik. Kamu bisa membaca lebih tentang ini di manual.

Karakter spesial

Masih mungkin untuk membuat string dengan banyak baris menggunakan petik satu atau petik dua dengan menggunakan ???karakter newline???, ditulis seperti berikut \n, yang menandakan baris baru:

let guestList = "Guests:\n * John\n * Pete\n * Mary";

alert(guestList); // list tamu yang dipisahkan per baris

Sebagai contoh, kedua baris berikut sama saja, hanya ditulis dengan cara yang berbeda:

let str1 = "Hello\nWorld"; // dua baris menggunakan "simbol baris baru"

// dua baris menggunakan backtick
let str2 = `Hello
World`;

alert(str1 == str2); // true

Ada karakter spesial lain, tetapi mereka jarang digunakan

Berikut adalah daftar lengkapnya:

Character Description
\n Baris baru
\r Carriage return: tidak digunakan sendiri. File teks milik di Windows menggunakan kombinasi dari dua karakter \r\n untuk menandakan baris baru.
\', \" Petik-petik
\\ Backslash
\t Tab
\b, \f, \v Backspace, Form Feed, Vertical Tab ??? tetap bisa digunakan untuk kompabilitas, sekarang sudah tidak digunakan.
\xXX Karakter unicode dengan nilai heksadesimal XX, misalnya '\x7A' itu sama saja dengan 'z'.
\uXXXX Sebuah simbol unicode dengan nilai heksadesimal XXXX di dalam encoding UTF-16, sebagai contoh \u00A9 ??? adalah sebuah unicode untuk simbol copyright ??. Simbol ini harus terdiri dari 4 digit heksadesimal.
\u{X???XXXXXX} (1 to 6 karakter heksadesimal) Sebuah simbol unicode dengan encoding UTF-32. Beberapa karakter langka menggunakan dua simbol unicode, yang memakan 4 byte. Dengan cara ini kita dapat menggunakan kode yang panjang.

Contoh dengan unicode:

alert( "\u00A9" ); // ??
alert( "\u{20331}" ); // ???, sebuah karakter mandarin (unicode panjang)
alert( "\u{1F60D}" ); // ????, sebuah simbol wajah tersenyum (unicode panjang lainnya)

Karakter-karakter spesial yang diawali dengan karakter backslash \ kadang dipanggil dengan sebutan ???escape character???.

Kita kadang dapat menggunakannya apabila kita ingin menggunakan petik di dalam string.

Misalnya:

alert( 'I\'m the Walrus!' ); // I'm the Walrus!

Seperti yang kita lihat, kita harus menambahkan backslash di depan petik yang di dalam string \', karena jika tidak petik tersebut akan menandakan akhir dari sebuah string.

Tentu saja, hanya jenis petik yang sama dengan penutup string yang perlu di ???escape???. Jadi, solusi yang lebih elegan yaitu mengganti petik satu menjadi petik dua atau backtick:

alert( `I'm the Walrus!` ); // I'm the Walrus!

Ingat bahwa backslash \ hanya dipakai untuk Javascript agar dapat membaca string dengan benar. Di dalam memori, string tidak memiliki \. Anda dapat melihatnya secara langsung pada contoh alert di atas.

Tetapi bagaimana jika kita ingin menampilkan backslash \ di dalam sebuah string?

Hal tersebut bisa dilakukan, tetapi kita harus menulisnya dua kali seperti ini \\:

alert( `The backslash: \\` ); // The backslash: \

Panjang string

Properti length memiliki panjang dari string:

alert( `My\n`.length ); // 3

Perlu diingat bahwa \n adalah sebuah karakter spesial, jadi panjang dari string adalah 3.

length adalah sebuah properti

Orang dengan latar belakang di bahasa pemrograman lain kadang salah mengetik str.length() alih-alih str.length. Hal tersebut tidak bekerja.

Perlu diingat bahwa str.length adalah properti numerik, bukan sebuah fungsi. Tidak perlu menambahkan kurung di belakangnya.

Mengakses karakter di dalam string

Untuk mengakses karakter pada posisi pos, digunakan kurung kotak [pos] atau dengan method str.charAt(pos). Karakter pertama dimulai dari posisi ke-0:

let str = `Hello`;

// karakter pertama
alert( str[0] ); // H
alert( str.charAt(0) ); // H

// karakter terakhir
alert( str[str.length - 1] ); // o

Kurung kotak adalah cara modern untuk mengakses sebuah karakter, sementara charAt ada karena alasan historis.

Perbedaan satu-satunya di antara mereka adalah apabila tidak ada karakter yang ditemukan, [] mengembalikan undefined, dan charAt mengembalikan string kosong:

let str = `Hello`;

alert( str[1000] ); // undefined
alert( str.charAt(1000) ); // '' (string kosong)

Kita juga bisa mengakses karakter per karakter menggunakan sintaks for..of:

for (let char of "Hello") {
  alert(char); // H,e,l,l,o (char bernilai "H", lalu "e", lalu "l", dan seterusnya)
}

String bersifat tidak dapat diubah

Nilai string tidak dapat diubah di Javascript. Tak mungkin mengubah sebuah karakter.

Mari kita coba buktikan:

let str = 'Hi';

str[0] = 'h'; // galat
alert( str[0] ); // tidak bekerja

Salah satu cara untuk mengatasi hal tersebut adalah untuk membuat string baru lalu memasukkan nilainya ke str.

Sebagai contoh:

let str = 'Hi';

str = 'h' + str[1]; // mengganti nilai string

alert( str ); // hi

Di bab ini kita akan melihat contoh lebih banyak dari ini.

Mengganti case

Metode toLowerCase() dan toUpperCase() mengganti case:

alert( 'Interface'.toUpperCase() ); // INTERFACE
alert( 'Interface'.toLowerCase() ); // interface

Atau, apabila kita hanya ingin sebuah karakter yang diubah menjadi huruf kecil:

alert( 'Interface'[0].toLowerCase() ); // 'i'

Mencari substring

Ada banyak cara untuk mencari sebuah substring di dalam sebuah string.

str.indexOf

Cara yang pertama yaitu str.indexOf(substr, pos).

Method ini mencari substr di dalam str, mulai dari posisi pos yang diberikan, dan mengembalikan posisi dimana substring ditemukan atau -1 jika tidak ditemukan.

Misalnya:

let str = 'Widget with id';

alert( str.indexOf('Widget') ); // 0, karena 'Widget' ditemukan di awal string
alert( str.indexOf('widget') ); // -1, tidak ditemukan, karena pencarian bersifat case-sensitive

alert( str.indexOf("id") ); // 1, "id" ditemukan pada posisi 1 (..idget with id)

Parameter kedua yang opsional memperbolehkan kita untuk mencari dari posisi yang ditentukan.

Misalnya, kemunculan pertama "id" ada di posisi 1. Untuk mencari kemunculan berikutnya, ayo kita mulai pencarian dari posisi 2:

let str = 'Widget with id';

alert( str.indexOf('id', 2) ) // 12

Jika kita tertarik dengan semua kemunculan, kita bisa menjalankan indexOf di dalam loop. Setiap panggilan dibuat dengan posisi setelah kemunculan sebelumnya:

let str = 'As sly as a fox, as strong as an ox';

let target = 'as'; // mari kita cari

let pos = 0;
while (true) {
  let foundPos = str.indexOf(target, pos);
  if (foundPos == -1) break;

  alert( `Found at ${foundPos}` );
  pos = foundPos + 1; // lanjutkan pencarian dari posisi berikutnya
}

Algoritma yang sama dapat ditulis lebih pendek:

let str = "As sly as a fox, as strong as an ox";
let target = "as";

let pos = -1;
while ((pos = str.indexOf(target, pos + 1)) != -1) {
  alert( pos );
}
str.lastIndexOf(substr, position)

Ada juga method yang hampir sama str.lastIndexOf(substr, position) yang mencari dari akhir sebuah string sampai ke awalnya.

Cara tersebut akan menemukan kemunculan dalam urutan yang terbalik.

Ada sedikit kerepotan dalam menggunakan indexOf di dalam if. Kita tidak dapat menggunakannya seperti ini:

let str = "Widget with id";

if (str.indexOf("Widget")) {
    alert("We found it"); // tidak bekerja!
}

Contoh di atas tidak bekerja karena str.indexOf("Widget") mengembalikan 0 (artinya kemunculan ditemukan di awal string). if menganggap 0 sebagai false.

Jadi, kita harus mengecek dengan nilai -1, seperti ini:

let str = "Widget with id";

if (str.indexOf("Widget") != -1) {
    alert("We found it"); // kalau sekarang berhasil!
}

Trik bitwise NOT

Salah satu trik lama yang digunakan disini adalah operator bitwise NOT ~. Operator ini mengubah angka menjadi integer 32-bit (menghilangkan bagian desimal jika ada) lalu menegasikan semua bit pada representasi binernya.

Dalam praktik, hal tersebut berarti: untuk integer 32-bit ~n sama dengan -(n+1).

Sebagai contoh:

alert( ~2 ); // -3, sama dengan -(2+1)
alert( ~1 ); // -2, sama dengan -(1+1)
alert( ~0 ); // -1, sama dengan -(0+1)
alert( ~-1 ); // 0, sama dengan -(-1+1)

Seperti yang kita lihat, ~n bernilai nol apabila n == -1 (untuk semua signed integer n).

Jadi, pengecekan di dalam if ( ~str.indexOf("...") ) bernilai benar apabila hasil dari indexOf tidak bernilai -1. Dengan kata lain, jika kemunculan ditemukan.

Orang-orang menggunakannya untuk memperpendek pengecekan indexOf:

let str = "Widget";

if (~str.indexOf("Widget")) {
  alert( 'Found it!' ); // bekerja
}

Biasanya tidak direkomendasikan untuk menggunakan fitur bahasa dengan cara yang tidak jelas, tetapi trik ini biasa digunakan di kode yang kuno, jadi kita harus memahaminya.

Ingat: if (~str.indexOf(...)) dibaca sebagai ???if ditemukan???.

Untuk lebih detail, karena bilangan yang besar dipotong menjadi 32-bit oleh operator ~, ada angka lain yang memberikan hasil 0, angka yang terkecil yaitu ~4294967295=0. Hal tersebut menyebabkan trik ini benar apabila string yang dites tidak sepanjang itu.

Kita dapat melihat bahwa trik ini hanya digunakan di kode yang kuno, karena Javascript modern menyediakan method .includes (lihat di bawah).

includes, startsWith, endsWith

Method yang lebih modern str.includes(substr, pos) mengembalikan true/false tergantung dengan apakah str mengandung substr di dalamnya.

Ini adalah pilihan yang cocok apabila kita hanya perlu mengetes apakah substr ada, tetapi tidak memerlukan posisinya:

alert( "Widget with id".includes("Widget") ); // true

alert( "Hello".includes("Bye") ); // false

Parameter opsinal kedua dari str.includes adalah posisi dimana pencarian mulai dilakukan:

alert( "Widget".includes("id") ); // true
alert( "Widget".includes("id", 3) ); // false, dari posisi 3 tidak ditemukan "id"

Method str.startsWith dan str.endsWith melakukan fungsi seperti namanya:

alert( "Widget".startsWith("Wid") ); // true, "Widget" diawali oleh "Wid"
alert( "Widget".endsWith("get") ); // true, "Widget" diakhiri oleh "get"

Mengambil substring

Ada 3 cara untuk mengambil sebuah substring di Javascript: substring, substr dan slice.

str.slice(start [, end])

Mengembalikan bagian dari string dari start sampai (tapi tidak termasuk) end.

Sebagai contoh:

let str = "stringify";
alert( str.slice(0, 5) ); // 'strin', substring dari posisi 0 sampai 5 (tidak termasuk 5)
alert( str.slice(0, 1) ); // 's', dari 0 sampai 1, tetapi tidak termasuk 1, jadi hanya karakter pada posisi 0

Jika tidak ada parameter kedua, maka slice akan mengambil semua bagian dari start sampai akhir string:

let str = "stringify";
alert( str.slice(2) ); // ringify, dari posisi kedua sampai terakhir

Nilai negatif untuk start/end juga bisa digunakan. Nilai negatif berarti posisinya dihitung dari akhir string:

let str = "stringify";

// mulai dari posisi ke-4 dari kanan, berakhir di posisi pertama dari kanan
alert( str.slice(-4, -1) ); // gif
str.substring(start [, end])

Mengembalikan bagian dari string di antara start dan end.

Method ini hampir sama dengan slice, tetapi nilai start boleh lebih besar daripada end.

Sebagai contoh:

let str = "stringify";

// kedua ini sama saja untuk substring
alert( str.substring(2, 6) ); // "ring"
alert( str.substring(6, 2) ); // "ring"

// ...tetapi tidak untuk slice:
alert( str.slice(2, 6) ); // "ring" (sama)
alert( str.slice(6, 2) ); // "" (string kosong)

Parameter negatif tidak didukung (tidak seperti slice), mereka dianggap sebagai 0.

str.substr(start [, length])

Mengembalikan substring dari start, dengan panjang length.

Dibandingkan dengan cara-cara sebelumnya, cara ini memperbolehkan kita untuk menyebutkan length alih-alih posisi akhir dari string:

let str = "stringify";
alert( str.substr(2, 4) ); // ring, dari posisi ke-2 ambil 4 karakter

Parameter pertama mungkin bernilai negatif, untuk menghitung dari akhir string:

let str = "stringify";
alert( str.substr(-4, 2) ); // gi, dari posisi ke-4 ambil 2 karakter

Mari kita review cara-cara tersebut untuk menghindari kebingungan:

method mengambil??? negatives
slice(start, end) dari start sampai end (tidak termasuk end) nilai negatif diperbolehkan
substring(start, end) antara start dan end nilai negatif berarti mean 0
substr(start, length) dari start ambil length karakter start negatif diperbolehkan
Cara mana yang harus kita gunakan?

Semuanya dapat melakukan pekerjaannya. Secara formal, substr memiliki kekurangan: fungsi ini tidak tercantum di spesifikasi inti Javascript, tetapi di Annex B, yang mencakup hanya fitur browser yang ada karena alasan historis. Jadi, environment non-browser mungkin gagal untuk mendukungnya. Tetapi dalam praktik fungsi ini bekerja di mana saja.

Dibandingkan dengan dua varian yang lain, slice lebih fleksibel, karena memperbolehkan parameter negatif dan lebih pendek untuk ditulis. Jadi, dari ketiga cara sudah cukup untuk mengingat slice.

Membandingkan string

Seperti yang kita tahu dari bab Perbandingan, string dibandingkan karakter per karakter dengan urutan alfabet.

Akan tetapi, ada beberapa pengecualian.

  1. Huruf kecil selalu lebih besar dibanding huruf kapital:

    alert( 'a' > 'Z' ); // true
  2. Karakter dengan tanda diakritik ???tidak sesuai urutan???:

    alert( '??sterreich' > 'Zealand' ); // true

    Hal ini dapat menyebabkan hasil yang aneh apabila kita mengurutkan nama-nama negara. Biasanya orang mengharapkan Zealand muncul setelah ??sterreich di daftar.

Untuk memahami apa yang terjadi, mari kita review representasi internal string di Javascript.

Semua string menggunakan encoding UTF-16. Yaitu: setiap karakter memiliki masing-masing kode numerik. Ada method spesial yang memperbolehkan kita untuk mengambil karakter dari kode dan sebaliknya.

str.codePointAt(pos)

Mengembalikan kode untuk karakter pada posisi pos:

// karakter dengan case yang berbeda memiliki kode berbeda
alert( "z".codePointAt(0) ); // 122
alert( "Z".codePointAt(0) ); // 90
String.fromCodePoint(code)

Membuat sebuah karakter berdasarkan code numeriknya

alert( String.fromCodePoint(90) ); // Z

Kita juga dapat membuat karakter unicode dengan kode mereka menggunakan \u yang diikuti oleh kode heksadesimal:

// 90 bernilai 5a di dalam sistem heksadesimal
alert( '\u005a' ); // Z

Sekarang mari kita lihat karakter dengan kode di antara 65..220 (alfabet latin dan sedikit tambahan) dengan membuat string yang terdiri dari mereka:

let str = '';

for (let i = 65; i <= 220; i++) {
  str += String.fromCodePoint(i);
}
alert( str );
// ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~??????????
// ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????

Kan? Huruf kapital muncul pertama, lalu beberapa karakter spesial, lalu huruf kecil, dan ?? berada di dekat akhir string.

Sekarang terlihat jelas kenapa a > Z.

Karakter-karakter dibandingkan berdasarkan kode numeriknya. Kode yang lebih besar berarti karakter tersebut lebih besar. Kode untuk a (97) lebih besar dibandingkan dengan kode untuk Z (90).

  • Semua huruf kecil muncul setelah huruf kapital karena kode mereka lebih besar.
  • Beberapa karakter seperti ?? terpisah dari alfabet utama. Disini, kodenya lebih besar dibandingkan semua karakter dari a to z.

Perbandingan yang benar

Algoritma yang ???benar??? untuk melakukan perbandingan string lebih kompleks dari kelihatannya, setiap bahasa memiliki alfabet mereka masing-masing.

Jadi, browser harus tahu bahasa yang digunakan untuk perbandingan.

Beruntungnya, semua browser modern (IE10- memerlukan library tambahan ) mendukung standar internasionalisasi ECMA 402.

Hal tersebut menyediakan cara spesial untuk membandingkan stringi di berbeda bahasa, mengikuti peraturan mereka.

Method str.localeCompare(str2) mengembalikan sebuah interger yang menandakan apakah str lebih kecil, sama dengan atau lebih besar dari str2 menurut peraturan-peraturan bahasa:

  • Mengembalikan nilai negatif jika str lebih kecil dibandingkan str2
  • Mengembalikan nilai positif jika str lebih besar dibandingkan str2
  • Mengembalikan 0 apabila mereka sama.

Seperti contoh:

alert( '??sterreich'.localeCompare('Zealand') ); // -1

Method ini sebenarnya menerima 2 argumen tambahan yang disebutkan di dokumentasi, yang memperbolehkan untuk menyebutkan bahasa (yang biasanya diambil dari environment, urutan huruf bergantung dari bahasa) dan menyebutkan peraturan-peraturan tambahan seperti case sensitivity atau apakah "a" and "a??" dianggap sama dan seterusnya.

Bagian internal dari unicode

Pengetahuan lanjutan

Bagian ini membahas lebih dalam tentang bagian internal string. Pengetahuan ini akan berguna apabila Anda akan berurusan dengan emoji, simbol matematika, hieroglif, atau simbol-simbol lain yang langka.

Anda dapat melewati bagian ini jika tidak berurusan dengan mereka.

Surrogate pairs

Semua karakter yang sering digunakan memiliki kode 2-byte. Huruf di kebanyakan negara eropa, angka, dan bahkan kebanyakan hieroglif, memiliki representasi 2-byte.

Tetapi 2 byte hanya memperbolehkan 65536 kombinasi dan itu tidak cukup untuk semua kombinasi simbol. Jadi simbol-simbol yang langka menggunakan encoding dengan sepasang karakter 2-byte yang disebut ???surrogate pair???.

Panjang dari simbol tersebut adalah 2:

alert( '????'.length ); // 2, SIMBOL MATEMATIKA X BESAR
alert( '????'.length ); // 2, MUKA DENGAN TANGISAN BAHAGIA
alert( '????'.length ); // 2, karakter mandarin

Perlu diingat bahwa surrogate pair tidak ada pada saat Javascript dibuat, oleh karena itu fitur ini tidak diproses secara benar oleh bahasa ini!

Kita sebenarnya memiliki sebuah simbol di setiap string di atas, tetapi length menunjukkan panjang 2.

String.fromCodePoint dan str.codePointAt adalah beberapa method yang menangani surrogate pair dengan benar. Belum lama ini mereka muncul di bahasa ini. Sebelum mereka, hanya ada String.fromCharCode dan str.charCodeAt. Method-method tersebut sebenarnya sama saja dengan fromCodePoint/codePointAt, tetapi tidak bisa digunakan untuk surrogate pair.

Mengambil sebuah simbol terkadang agak susah, karena surrogate pair diperlakukan sebagai dua karakter:

alert( '????'[0] ); // simbol aneh...
alert( '????'[1] ); // ...bagian dari surrogate pair

Perlu diingat bahwa bagian dari surrogate pair tidak memiliki arti tanpa pasangan yang lain. Jadi contoh di atas menampilkan karakter aneh.

Secara teknis, surrogate pair juga dapat dideteksi berdasarkan kode mereka, jika sebuah karakter memiliki kode di antara 0xd800..0xdbff, maka karakter ini adalah bagian pertama dari surrogate pair. Karakter selanjutnya (bagian kedua) harus berada di antara 0xdc00..0xdfff. Interval ini sudah dipesan secara khusus untuk surrogate pair oleh standar.

Pada kasus diatas:

// charCodeAt is not surrogate-pair aware, so it gives codes for parts

alert( '????'.charCodeAt(0).toString(16) ); // d835, diantara 0xd800 dan 0xdbff
alert( '????'.charCodeAt(1).toString(16) ); // dcb3, diantara 0xdc00 dan 0xdfff

Anda akan menemukan cara lain untuk bertanganan dengan surrogate pair nanti di bab Iterables / Bisa di iterasi. Mungkin juga ada library-library yang untuk hal tersebut, tetapi tidak ada yang cukup terkenal untuk disarankan di sini.

Tanda diakritik dan normalisasi

Di banyak bahasa terdapat simbol-simbol yang terdiri dari huruf dasar dengan tanda di atas/bawahnya.

Sebagai contoh, karakter a dapat menjadi huruf dasar untuk: ??????????????. Kebanyakan karakter ???komposit??? memiliki kode mereka sendiri di tabel UTF-16. Hal tersebut tidak selalu terjadi, karena terlalu banyak kemungkinan kombinasi.

Untuk mendukung komposisi yang fleksibel, UTF-16 memperbolehkan kita untuk menggunakan beberapa karakter unicode: sebuah huruf dasar yang diikuti oleh satu atau lebih karakter ???tanda??? yang ???menghiasinya???.

Sebagai contoh, jika kita memiliki S diikuti dengan karakter spesial ???titik di atas??? (kode \u0307), maka akan ditampilkan S??.

alert( 'S\u0307' ); // S??

Jika kita memerlukan tanda tambahan di atas huruf (atau di bawahnya) ??? tidak masalah, tambahkan saja karakter tanda yang diperlukan.

Sebagai contoh, jika kita tambahkan sebuah karakter ???titik di bawah??? (kode \u0323), maka kita akan mendapatkan ???S dengan titik di atas dan di bawah???: S????.

Sebagai contoh:

alert( 'S\u0307\u0323' ); // S????

Hal tersebut memberikan banyak fleksibilitas, tetapi juga masalah yang menarik: dua karakter mungkin terlihat sama, tetapi dapat direpresentasikan dengan komposisi unicode yang berbeda.

Sebagai contoh:

let s1 = 'S\u0307\u0323'; // S????, S + titik di atas + titik di bawah
let s2 = 'S\u0323\u0307'; // S????, S + titik di atas + titik di bawah

alert( `s1: ${s1}, s2: ${s2}` );

alert( s1 == s2 ); // false walaupun karakter terlihat sama (?!)

Untuk menyelesaikan masalah ini, terdapat sebuah algoritma ???normalisasi unicode??? yang membuat setiap string menjadi satu bentuk ???normal???.

Algoritma tersebut diimplementasikan oleh str.normalize().

alert( "S\u0307\u0323".normalize() == "S\u0323\u0307".normalize() ); // true

Agak lucu bahwa di situasi kita normalize() menjadikan sekumpulan karakter dengan panjang 3 menjadi satu: \u1e68 (S dengan dua titik).

alert( "S\u0307\u0323".normalize().length ); // 1

alert( "S\u0307\u0323".normalize() == "\u1e68" ); // true

Pada kenyataan, hal ini tidak selalu berlaku. Contoh diatas berlaku karena simbol ??? is ???cukup sering digunakan???, jadi pembuat UTF-16 memasukkannya di tabel utama dan memberinya sebuah kode.

Jika Anda ingin belajar lebih lanjut tentang aturan normalisasi dan variasinya ??? mereka dideskripsikan di appendix Unicode standard: Unicode Normalization Forms, tetapi untuk kebanyakan kasus informasi yang terdapat di bagian ini sudah cukup.

Kesimpulan

  • Terdapat 3 jenis tanda petik. Backtick membolehkan string memiliki baris ganda dan menyisipkan expresi ${???}.
  • String di Javascript diencode menggunakan UTF-16.
  • Kita bisa memakai karakter seperti \n dan memasukkan karakter berdasarkan unicode mereka menggunakan \u....
  • Untuk mendapatkan karakter, gunakan: [].
  • Untuk mendapatkan substring, gunakan: slice atau substring.
  • Untuk mengubah case kecil/besar dari string, gunakan: toLowerCase/toUpperCase.
  • Untuk mencari substring, gunakan: indexOf, atau includes/startsWith/endsWith untuk pengecekan sederhana.
  • Untuk membandingkan string mengikuti bahasa, gunakan localeCompare, jika tidak mereka akan dibandingkan berdasarkan kode karakter.

Ada beberapa metode string lain yang berguna:

  • str.trim() ??? menghilangkan (???memotong???) spasi dari awal dan akhir dari sebuah string.
  • str.repeat(n) ??? mengulang string sebanyak n kali.
  • ???dan masih banyak lagi yang dapat ditemukan di dalam manual.

String juga memiliki method-method untuk mencari/mengganti dengan ekspresi reguler (regular expression). Tetapi itu adalah topik yang luas, jadi topik ini dibahas di bagiannya sendiri Regular expressions.

Tugas

Tulislah sebuah fungsi ucFirst(str) yang mengembalikan str dengan karakter pertama yang besar, sebagai contoh:

ucFirst("john") == "John";

Buka sandbox dengan tes.

Kita tidak dapat ???mengganti??? karakter pertama, karena di Javascript string bersifat tidak dapat berubah.

Tetapi kita dapat membuat sebuah string baru berdasarkan yang sudah ada, dengan karakter pertama yang besar:

let newStr = str[0].toUpperCase() + str.slice(1);

Tetapi ada sedikit masalah. Jika str bernilai kosong, maka str[0] bernilai undefined, dan undefined tidak memiliki method toUpperCase(). Hal tersebut yang menyebabkan error.

Ada dua cara di sini:

  1. Gunakan str.charAt(0), karena method ini selalu mengembalikan string (mungkin kosong).
  2. Tambahkan pengecekan string kosong.

Berikut adalah cara yang kedua:

function ucFirst(str) {
  if (!str) return str;

  return str[0].toUpperCase() + str.slice(1);
}

alert( ucFirst("john") ); // John

Buka solusi dengan tes di sandbox.

Tulislah sebuah fungsi checkSpam(str) yang mengembalikan true apabila str mengandung ???viagra??? atau ???XXX???, jika tidak kembalikan false.

Fungsi yang ditulis harus bersifat case-insensitive:

checkSpam('buy ViAgRA now') == true
checkSpam('free xxxxx') == true
checkSpam("innocent rabbit") == false

Buka sandbox dengan tes.

Untuk membuat pencarian bersifat case-insensitive, mari kita ubah string menjadi huruf kecil sebelum kita lakukan pencarian:

function checkSpam(str) {
  let lowerStr = str.toLowerCase();

  return lowerStr.includes('viagra') || lowerStr.includes('xxx');
}

alert( checkSpam('buy ViAgRA now') );
alert( checkSpam('free xxxxx') );
alert( checkSpam("innocent rabbit") );

Buka solusi dengan tes di sandbox.

Buatlah sebuah fungsi truncate(str, maxlength) yang mengecek panjang dari str dan, apabila panjangnya melebihi maxlength ??? ganti akhir dari str menjadi karakter elipsis "???", supaya panjangnya sama dengan maxlength.

Hasil kembalian dari fungsi seharusnya string yang dipotong (jika diperlukan).

Sebagai contoh:

truncate("What I'd like to tell on this topic is:", 20) = "What I'd like to te???"

truncate("Hi everyone!", 20) = "Hi everyone!"

Buka sandbox dengan tes.

Panjang maksimum adalah maxlength, jadi kita perlu memotongnya menjadi lebih pendek, untuk memberi tempat bagi elipsis.

Perlu diperhatikan bahwa elipsis hanyalah sebuah karakter unicode, bukan tiga karakter titik.

function truncate(str, maxlength) {
  return (str.length > maxlength) ?
    str.slice(0, maxlength - 1) + '???' : str;
}

Buka solusi dengan tes di sandbox.

Kita memiliki uang dalam bentuk "$120". Yaitu: tanda dolar muncul pertama, lalu diikuti oleh angka.

Buatlah sebuah fungsi extractCurrencyValue(str) yang mengambil nilai dari bagian angka string tersebut dan mengembalikannya.

Sebagai contoh:

alert( extractCurrencyValue('$120') === 120 ); // true

Buka sandbox dengan tes.

function extractCurrencyValue(str) {
  return +str.slice(1);
}

Buka solusi dengan tes di sandbox.

Peta tutorial