Mengenal JavaScript
Sejarah singkat, let vs const vs var, dan console.
Mengenal JavaScript
- 01Pahami konsepnya · 3
- 02Coba contoh kode · 1
- 03Latihan singkat
Sejarah singkat, let vs const vs var, dan console.
JavaScript diciptakan Brendan Eich di Netscast tahun 1995 — dalam 10 hari. Awalnya bernama Mocha lalu LiveScript sebelum menjadi JavaScript. Hari ini ia satu-satunya bahasa yang berjalan native di semua browser, dan dengan Node.js ia juga berjalan di server, desktop (Electron), dan bahkan IoT.
Tiga cara mendeklarasikan variabel: let untuk nilai yang bisa berubah, const untuk nilai tetap (wajib diisi langsung), dan var — warisan lama yang scope-nya function-level dan punya quirk hoisting. Aturan praktis: selalu pakai const kecuali memang perlu re-assign; gunakan let untuk penghitung dan akumulator; hindari var.
console.log() adalah teman debugging terdekat: cetak apa pun ke console browser atau terminal. Ada juga console.table() untuk array of object, console.warn() untuk peringatan, dan console.error() untuk error. Di Judge0 editor, console.log keluar sebagai stdout.
const tidak berarti "beku" — objek const masih bisa dimodifikasi propertinya; const hanya mencegah re-assignment variabel itu sendiri. typeof mengembalikan string nama tipe.
Uji: cetak `Halo, JavaScript!` lalu `Siap belajar!`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
const nama = "Ari"; // tidak bisa re-assign
let poin = 0; // bisa
poin += 10;
console.log(`${nama} punya ${poin} poin`);
console.log(typeof nama, typeof poin); // string number7 tipe primitif + object, typeof, konversi otomatis.
JavaScript punya 7 tipe primitif: string, number (semua angka — bulat dan desimal — adalah number, tidak ada int/float terpisah), boolean, undefined (belum diisi), null (sengaja kosong), symbol (kunci unik), dan bigint (angka sangat besar, 123n). Selain primitif, semuanya adalah object — termasuk array, function, dan date.
typeof memeriksa tipe: typeof "halo" → "string", typeof 42 → "number", typeof undefined → "undefined", typeof null → "object" (bug historis yang tak akan diperbaiki). Untuk array: Array.isArray([1,2]) → true.
Konversi otomatis terjadi terus-menerus dan menjadi sumber bug: "5" + 3 menghasilkan "53" (string concat), "5" - 3 menghasilkan 2 (number subtract), "5" * 3 menghasilkan 15. Tanda + memihak string, tanda aritmetika lain memihak number. Konversi eksplisit lebih aman: Number("42"), String(42), Boolean(0) → false.
Jebakan klasik: "5" + 3 merangkai menjadi "53" karena + memihak string bila salah satu operand string. Boolean("0") adalah true — string "0" tidak kosong, beda dari number 0 yang falsy.
Uji: konversi "42" ke number, tambah 8 — harus `50`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
let teks = "42";
let angka = Number(teks);
console.log(teks + "5"); // "425" — string concat
console.log(angka + 5); // 47 — number add
console.log(typeof null); // "object" (bug!)
console.log(Array.isArray([1])); // true
console.log(Boolean(0), Boolean("")); // false false
console.log(Boolean("0")); // true — string tidak kosong!Aritmetika, perbandingan, logika, ternary, nullish, optional chaining.
Operator aritmetika standar: + - * / % ** — semuanya number. Perbandingan == vs ===: yang pertama melakukan type coercion ("5" == 5 → true), yang kedua membandingkan tipe DAN nilai ("5" === 5 → false). Selalu gunakan === dan !==.
Tiga operator modern: ternary kondisi ? benar : salah; nullish coalescing ?? memberi cadangan hanya saat null/undefined (bukan 0 atau ""); optional chaining ?. mengakses properti dalam-dalam tanpa error bila ada yang null di tengah jalan.
Logika: && (dan — berhenti di falsy pertama), || (atau — berhenti di truthy pertama), ! (not). Keduanya mengembalikan OPERAND, bukan boolean — "halo" || "dunia" menghasilkan "halo". Sifat short-circuit ini dipakai untuk guard clause dan default value.
Perbedaan || vs ?? penting: || mengganti SEMUA falsy (0, "", false) dengan cadangan; ?? hanya mengganti null dan undefined. Optional chaining ?. mencegah TypeError saat mengakses properti objek yang mungkin null.
Uji: cetak 7**2, 7%2, 7/2 — harus `49 1 3.5`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
const umur = 17;
const nama = umur >= 17 ? "dewasa" : "anak";
// nullish vs or
const nol = 0;
console.log(nol || 10); // 10 — 0 falsy
console.log(nol ?? 10); // 0 — 0 bukan null/undefined
// optional chaining
const user = { profil: { kota: "Bandung" } };
console.log(user.profil?.kota); // Bandung
console.log(user.alamat?.kota); // undefined — tidak error
// strict equality
console.log("5" === 5); // false
console.log(5 === 5); // trueif/else, switch, ternary, truthy/falsy.
if/else JavaScript sama seperti bahasa lain, dengan satu keunikan: kondisi TIDAK harus boolean. JavaScript mengubah (coerce) nilai apa pun menjadi truthy atau falsy. Nilai falsy: false, 0, -0, 0n, "" (string kosong), null, undefined, dan NaN. SEMUA nilai lain truthy — termasuk "0", "false", [], {} (array dan object kosong tetap truthy!).
switch cocok untuk satu nilai banyak cabang. JavaScript switch memakai strict equality (===) tanpa coercion — switch berperilaku seperti yang diharapkan. Tanpa break, eksekusi jatuh (fallthrough) ke case berikutnya.
Ternary untuk assignment kondisional satu baris: const status = nilai >= 70 ? "lulus" : "remedial". Bisa bersarang tapi lebih dari dua tingkat sebaiknya if/else biasa.
Array kosong [] dan object kosong {} adalah TRUTHY — sering mengejutkan pemula dari bahasa lain. "0" string juga truthy karena tidak kosong. Hanya enam nilai falsy yang sudah disebut.
Uji: nilai 82 → `B`; ganti ke 60 → `C`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
const nilai = 82;
if (nilai >= 90) console.log("A");
else if (nilai >= 80) console.log("B");
else console.log("C");
// truthy/falsy
console.log(Boolean([])); // true — array kosong truthy!
console.log(Boolean("0")); // true
console.log(Boolean(0)); // false
// switch
const hari = 3;
switch (hari) {
case 1: console.log("Senin"); break;
case 2: console.log("Selasa"); break;
case 3: console.log("Rabu"); break;
default: console.log("?");
}for, while, for-of, for-in, break/continue.
JavaScript punya beberapa bentuk loop. for klasik dengan penghitung: for (let i = 0; i < 5; i++). while dan do-while seperti biasa. Yang membedakan JavaScript dari Go/C#: for-of menyusuri ITERABLE (array, string, Map, Set) memberi nilai langsung; for-in menyusuri PROPERTI objek memberi nama kunci.
for-of adalah pilihan utama untuk array: for (const buah of buahList) memberi elemen, bukan indeks. Bila butuh keduanya: for (const [i, buah] of buahList.entries()). for-in HANYAU untuk objek — memakainya di array menghasilkan indeks sebagai STRING dan menyertakan properti tambahan.
break keluar dari loop, continue lompat ke iterasi berikutnya. Label break outerLoop keluar dari loop bersarang — JavaScript mendukung label.
for-of memberi NILAI (buah), for-in memberi KUNCI (nama properti). entries() menggabung keduanya dalam destructuring [i, nilai]. Jangan pakai for-in untuk array.
Uji: jumlahkan 1-100 dengan for — harus `5050`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
// for klasik
for (let i = 1; i <= 5; i++) console.log(i);
// for-of untuk array
const buah = ["apel", "mangga", "jeruk"];
for (const b of buah) console.log(b);
// entries: indeks + elemen
for (const [i, b] of buah.entries()) console.log(`${i}: ${b}`);
// for-in untuk objek
const data = { nama: "Ari", umur: 17 };
for (const kunci in data) console.log(kunci, data[kunci]);Template literal, method umum, tag template.
Template literal (backtick) adalah cara modern memformat string: `Halo ${nama}, umur ${umur}` menyisipkan ekspresi langsung dalam kurung siku. Mendukung multi-baris tanpa \n, nested template `${nilai >= 70 ? "lulus" : "gagal"}`, dan tag template untuk pemrosesan khusus.
Method penting: length (properti, bukan method), toUpperCase/toLowerCase, trim, split(","), join(" - "), includes("halo"), startsWith/endsWith, replace("lama","baru"), replaceAll, padStart/padEnd, repeat(n), slice(a,b), indexOf.
String di JavaScript IMMUTABLE — method menghasilkan string baru, tidak mengubah aslinya. Tidak ada charAt yang mengubah; s[0] = "x" error. Iterasi: for (const c of "halo") memberi tiap karakter.
Template literal menggantikan rangkaian + yang rapuh. padStart berguna untuk format angka (kode, jam). String method menghasilkan string BARU — asli tidak berubah karena immutable.
Uji: `"halo dunia".toUpperCase().split(" ")` — harus `["HALO","DUNIA"]`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
const nama = "Ari";
const umur = 17;
console.log(`Halo ${nama}, tahun depan ${umur + 1}`);
// multi-baris
const puisi = `Baris pertama
Baris kedua`;
console.log(puisi);
// method
console.log(" halo ".trim());
console.log("a,b,c".split(","));
console.log("Belajar".toUpperCase());
console.log("5".padStart(3, "0")); // 005
console.log("ab".repeat(3)); // abababparseInt, parseFloat, NaN, Infinity, Math object, Number formatting.
JavaScript tidak membedakan int dan float — semua number adalah IEEE 754 double. Ini menghasilkan jebakan klasik: 0.1 + 0.2 === 0.3 adalah FALSE karena floating-point imprecision. Solusinya: Number.EPSILON untuk perbandingan, atau bekerja dalam satuan integer (sen, bukan rupiah).
NaN (Not a Number) adalah hasil operasi matematika gagal: 0/0, "abc" * 2, Math.sqrt(-1). NaN === NaN adalah FALSE — satu-satunya nilai yang tidak sama dengan dirinya. Periksa dengan isNaN() atau Number.isNaN(). Infinity hasil pembagian dengan nol: 1/0 → Infinity.
Math object: Math.round, Math.floor, Math.ceil, Math.abs, Math.max/min, Math.random (0-1 eksklusif), Math.pow, Math.sqrt, Math.trunc. Format angka: toFixed(2) untuk dua desimal (menghasilkan STRING), toLocaleString("id-ID") untuk pemisah ribuan.
Jebakan 0.1+0.2 bukan bug JavaScript — semua bahasa IEEE 754 mengalaminya. toFixed mengembalikan STRING; Number(...) kembali ke number. parseInt berhenti di karakter non-angka pertama.
Uji: Math.max(3,8,1,9,4) — harus `9`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false!
console.log(Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON); // true
console.log(parseInt("42px")); // 42
console.log(parseFloat("3.14m")); // 3.14
console.log(Number("42")); // 42
console.log(Math.floor(3.7), Math.ceil(3.2)); // 3 4
console.log((1234567).toLocaleString("id-ID")); // 1.234.567
console.log((3.14159).toFixed(2)); // "3.14" — string!Kunci unik, well-known symbols, dan use case-nya.
Symbol adalah tipe primitif ke-7 yang selalu UNIK: Symbol() === Symbol() adalah false, meski deskripsinya sama. Kegunaan utamanya: kunci properti objek yang dijamin tidak bertabrakan dengan properti lain — cocok untuk "hidden" metadata.
Well-known symbols adalah konstanta built-in yang mengubah perilaku objek: Symbol.iterator membuat objek bisa di-for-of; Symbol.toPrimitive mengatur konversi; Symbol.hasInstance menyesuaikan instanceof. Kamu jarang menulisnya langsung, tapi JavaScript memakainya di balik layar untuk spread, destructuring, dan for-of.
Praktisnya: Symbol paling sering ditemui sebagai Symbol.iterator saat membuat custom iterable, atau sebagai pengaman properti "private" di library yang tidak boleh diakses dari luar.
Symbol sebagai kunci objek tidak muncul di Object.keys() atau JSON.stringify — tersembunyi dari iterasi normal. Computed key [ID]: menggunakan symbol sebagai nama properti.
Uji: Symbol("a") === Symbol("a") — harus `false`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
const unik1 = Symbol("id");
const unik2 = Symbol("id");
console.log(unik1 === unik2); // false — selalu unik
// sebagai kunci yang aman dari tabrakan
const ID = Symbol("userId");
const user = { [ID]: 42, nama: "Ari" };
console.log(user[ID]); // 42
console.log(Object.keys(user)); // ["nama"] — symbol tersembunyi
// well-known: iterator
const angka = {
*[Symbol.iterator]() {
yield 1; yield 2; yield 3;
}
};
console.log([...angka]); // [1, 2, 3]Tiga cara mendefinisikan fungsi + parameter default + rest.
Tiga bentuk fungsi: function declaration (hoisted — bisa dipanggil sebelum dideklarasi), function expression (tidak hoisted), dan arrow function (ringkas, this dari scope luar). Bentuk declaration: function tambah(a, b) { return a + b; }. Arrow: const tambah = (a, b) => a + b.
Arrow function menghilangkan kata function dan return bila isinya satu ekspresi: n => n * 2. Tanpa kurung kurawal, hasil ekspresi otomatis dikembalikan. Dengan kurung kurawal, return wajib eksplisit: n => { return n * 2; }.
Parameter default: function sapa(nama = "Tamu") — nilai dipakai bila undefined. Rest parameter: function jumlah(...angka) mengumpulkan sisa argumen menjadi array. Keduanya bekerja di semua bentuk fungsi.
Arrow satu-ekspresi tanpa kurung kurawal otomatis return. rata memakai rest parameter (...angka) yang mengubah tiga argumen menjadi array untuk di-reduce.
Uji: arrow kuadrat(7) — harus `49`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
// declaration — hoisted
function tambah(a, b) { return a + b; }
// arrow — satu ekspresi, return implisit
const kali = (a, b) => a * b;
// arrow dengan body
const gabung = (a, b) => {
const hasil = `${a} ${b}`;
return hasil;
};
// default + rest
const sapa = (nama = "Tamu") => `Halo ${nama}!`;
const rata = (...angka) => angka.reduce((a, b) => a + b, 0) / angka.length;
console.log(tambah(3, 5), kali(3, 5));
console.log(sapa(), sapa("Ari"));
console.log(rata(10, 20, 30));Lexical scope, closure, IIFE — fondasi JavaScript.
Scope adalah jangkauan variabel. JavaScript punya three scope: global, function, dan block (since ES6 — let/const di dalam {} adalah block-scoped). var TIDAK block-scoped — ia bocor keluar dari if/for ke seluruh function. Ini alasan utama menghindari var.
Closure adalah fungsi yang "mengingat" variabel dari scope pembuatnya meski scope itu sudah selesai. function buatPenghitung() { let hitung = 0; return () => ++hitung; } — fungsi yang dikembalikan menutup (close over) variabel hitung. Ini cara JavaScript membuat private variable, counter, dan currying.
IIFE (Immediately Invoked Function Expression): (function() { ... })() — pola lama untuk membuat scope terisolasi sebelum ada module. Masih berguna untuk menghindari polusi global scope di kode legacy.
hitung tidak bisa diakses dari luar — hanya method naik/turun/nilai yang menutupnya. Ini pola module pattern berbasis closure. var bocor keluar dari if karena function-scoped, let dalam terkunci karena block-scoped.
Uji: closure counter mulai 10 — c.naik() harus `11`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
// closure: penghitung
function buatPenghitung() {
let hitung = 0; // private — tertutup dalam closure
return {
naik: () => ++hitung,
turun: () => --hitung,
nilai: () => hitung,
};
}
const c = buatPenghitung();
c.naik(); c.naik(); c.naik();
console.log(c.nilai()); // 3
console.log(c.hitung); // undefined — private!
// block scope
if (true) {
let dalam = "hanya di sini";
var bocor = "keluar dari if";
}
console.log(bocor); // "keluar dari if"
// console.log(dalam); // ReferenceErrorFungsi sebagai argumen — fondasi array method dan event handler.
Di JavaScript, fungsi adalah warga kelas satu: bisa disimpan ke variabel, dimasukkan ke array, dikirim sebagai argumen, dan dikembalikan dari fungsi lain. Fungsi yang MENERIMA fungsi lain sebagai argumen disebut higher-order function; fungsi yang DIKIRIM disebut callback.
Contoh paling dikenali: array method. buah.sort(cmp) menerima fungsi pembanding; button.addEventListener("click", handler) menerima fungsi yang dijalankan saat klik. setTimeout(fn, 1000) menerima callback yang berjalan setelah satu detik.
Menulis callback sendiri: cukup terima parameter bernama fungsi lalu panggil di dalam: function proses(data, sukses, gagal). Callback menerima segalanya — data, error, bahkan callback lain (callback hell saat bersarang terlalu dalam, yang dipecahkan Promise).
proses menerima dua callback: format mengubah data, tampilkan mencetak. Sort comparator (a, b) => b.poin - a.poin mengurutkan menurun berdasarkan poin.
Uji: saring([1..10], x => x % 3 === 0) — harus `[3,6,9]`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
// higher-order + callback
function proses(data, format, tampilkan) {
const hasil = format(data);
tampilkan(hasil);
}
proses(
[3, 1, 4, 1, 5],
(d) => [...d].sort((a, b) => a - b),
(h) => console.log("Hasil:", h)
);
// callback di sort dengan custom comparator
const siswa = [{ nama: "Budi", poin: 92 }, { nama: "Ari", poin: 85 }];
siswa.sort((a, b) => b.poin - a.poin);
console.log(siswa[0].nama); // BudiTransformasi dan agregasi koleksi secara fungsional.
Array method adalah cara JavaScript mengolah koleksi secara fungsional — tanpa loop manual. map(fn) mentransformasi tiap elemen menghasilkan array baru dengan panjang sama. filter(fn) menyaring elemen yang membuat fn true. reduce(fn, awal) menggabungkan seluruh array menjadi satu nilai — fn menerima (akumulator, elemen) dan mengembalikan akumulator baru.
Method pendukung: forEach(fn) menjalankan fn per elemen tanpa menghasilkan apa pun. find(fn) memberi elemen PERTAMA yang cocok. some(fn) true bila minimal satu cocok. every(fn) true bila SEMUA cocok. flat(depth) meratakan array bersarang. flatMap = map + flat(1).
Rantai method adalah pola yang powerful: arr.filter(x => x > 5).map(x => x * 2).reduce((a, b) => a + b, 0). Semua method non-mutating (kecuali sort dan reverse yang mengubah asli) — data sumber aman.
Rantai filter-map-reduce membaca seperti pipa: saring genap → kuadratkan → jumlahkan. reduce dengan nilai awal 0 menjumlah dari nol. find memberi elemen pertama yang cocok, bukan array.
Uji: map [1..5] dengan x*x lalu reduce — harus `55`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
const angka = [3, 8, 1, 9, 4, 6, 7, 2, 5];
// map: transformasi
const dobel = angka.map(x => x * 2);
console.log(dobel);
// filter: saring
const besar = angka.filter(x => x > 5);
console.log(besar);
// reduce: agregasi
const total = angka.reduce((acc, n) => acc + n, 0);
console.log(total); // 45
// chaining
const hasil = angka
.filter(x => x % 2 === 0)
.map(x => x * x)
.reduce((a, b) => a + b, 0);
console.log(hasil); // 4+64+16+36+4 = 124
// find, some, every
console.log(angka.find(x => x > 7)); // 8
console.log(angka.some(x => x > 8)); // true
console.log(angka.every(x => x > 0)); // trueBongkar array/objek ke variabel + kumpulkan sisanya.
Destructuring membongkar koleksi ke variabel dalam satu baris. Array: const [a, b, c] = [1, 2, 3]. Objek: const { nama, umur } = { nama: "Ari", umur: 17 }. Nama variabel HARUS sama dengan nama properti objek — kecuali di-rename: const { nama: n } = obj.
Rest (...): menangkap sisanya ke array/objek. const [pertama, ...sisanya] = [1,2,3,4] memberi pertama=1, sisanya=[2,3,4]. const { nama, ...lain } = obj memberi nama dan sisanya sebagai objek.
Spread (...): memecah koleksi jadi elemen terpisah. [...arr1, ...arr2] menggabung array; { ...obj1, ...obj2 } menggabung objek (yang belakangan menimpa). Spread juga untuk parameter fungsi: Math.max(...angka) memberi max dari seluruh array.
Destructuring objek: nama variabel harus sama dengan properti, KECUALI rename dengan titik dua (umur: tahun). Default value dengan = hanya aktif saat undefined. Spread menggantikan concat dan Object.assign.
Uji: destructuring [10,20,30] ke a,b — a+b harus `30`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
// array destructuring
const [pertama, kedua, ...sisanya] = [1, 2, 3, 4, 5];
console.log(pertama, kedua, sisanya); // 1 2 [3,4,5]
// object destructuring + rename + default
const { nama, umur: tahun = 0, kota = "Tidak ada" } = { nama: "Ari", umur: 17 };
console.log(nama, tahun, kota); // Ari 17 "Tidak ada"
// swap
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a, b); // 2 1
// spread
const arr1 = [1, 2], arr2 = [3, 4];
console.log([...arr1, ...arr2]); // [1,2,3,4]
const obj1 = { a: 1 }, obj2 = { b: 2 };
console.log({ ...obj1, ...obj2 }); // {a:1, b:2}
console.log(Math.max(...[3, 8, 1, 9])); // 9Tangkap error tanpa crash + throw dan custom Error.
JavaScript menangani error lewat exception: kode yang melempar Error menghentikan eksekusi sampai ditangkap try/catch. try membungkus kode berisiko; catch menangkap Error object (punya .message dan .name); finally berjalan SELALU — cocok untuk cleanup.
Tipe error: TypeError (akses properti null), ReferenceError (variabel tidak ada), SyntaxError (kode salah tulis), RangeError (nilai di luar rentang). Catch bisa memfilter: catch (e) { if (e instanceof TypeError) ... }.
throw melempar error sendiri: throw new Error("harga tidak boleh negatif"). Custom error: class ValidationError extends Error — menambah tipe spesifik untuk ditangkap berbeda.
Fungsi bagi melempar Error saat pembagi nol; catch menangkap dan menampilkan message. finally berjalan baik sukses maupun gagal. Custom error ValidationError menambah name spesifik untuk pembedaan.
Uji: throw new Error("test") → catch → cetak `test`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
function bagi(a, b) {
if (b === 0) throw new Error("Pembagi tidak boleh nol");
return a / b;
}
try {
console.log(bagi(10, 2));
console.log(bagi(10, 0));
} catch (e) {
console.log(`Gagal: ${e.message}`);
} finally {
console.log("Selalu berjalan.");
}
// custom error
class ValidationError extends Error {
constructor(nama) {
super(`${nama} tidak valid`);
this.name = "ValidationError";
}
}
try { throw new ValidationError("harga"); }
catch (e) { console.log(e.name, e.message); }yield untuk lazy sequence + fungsi yang memanggil diri sendiri.
Generator adalah fungsi yang menghasilkan nilai satu per satu dengan yield: function* fib() { yield 1; yield 1; yield 2; } — setiap yield menjeda eksekusi dan menyerahkan nilai. Panggil .next() untuk maju satu langkah, atau iterasi dengan for-of. Generator adalah fondasi for-of, spread, dan async/await.
Rekursi adalah fungsi yang memanggil dirinya sendiri pada versi lebih kecil dari masalahnya. Kasus klasik: faktorial(n) = n * faktorial(n-1), dengan kasus dasar faktorial(0) = 1. Tanpa kasus dasar, rekursi berputar selamanya (stack overflow).
Kegunaan generator: lazy evaluation (fibonacci tanpa menyimpan semua), pipeline data, dan custom iterable. Rekursi: tree traversal, divide-and-conquer, dan backtracking.
Generator menghasilkan fibonacci secara lazy — satu nilai per panggilan next(). Rekursi faktorial berhenti di kasus dasar n<=1. Tanpanya, stack overflow.
Uji: faktorial(6) rekursif — harus `720`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
// generator
function* fibonacci(n) {
let a = 0, b = 1;
for (let i = 0; i < n; i++) {
yield a;
[a, b] = [b, a + b];
}
}
for (const f of fibonacci(8)) {
console.log(f); // 0 1 1 2 3 5 8 13
}
// rekursi
function faktorial(n) {
if (n <= 1) return 1; // kasus dasar
return n * faktorial(n - 1); // kasus rekursif
}
console.log(faktorial(5)); // 120Iterable, iterator, next(), dan membuat custom iterable.
Protokol iterator adalah kontrak di balik for-of, spread, dan destructuring. Sebuah objek adalah ITERABLE bila punya method [Symbol.iterator]() yang mengembalikan ITERATOR — objek dengan method next() yang menghasilkan { value, done }. done true berarti habis.
Array, String, Map, Set, dan NodeList semua iterable — for-of bekerja pada mereka. Objek biasa TIDAK iterable secara bawaan; tambahkan *[Symbol.iterator]() untuk membuatnya.
Memahami protokol ini membuka pemahaman generator (yang otomatis menghasilkan iterator), async iterator (for-await-of), dan cara membuat struktur data custom yang bisa di-spread dan di-destructure.
next() menghasilkan objek { value, done } — done true menandakan iterator habis. Rentang custom bisa di-spread dan di-for-of karena punya Symbol.iterator generator.
Uji: class Rentang(1,5) dengan Symbol.iterator — [...r] harus `[1,2,3,4,5]`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
// manual: memanggil next()
const arr = ["a", "b", "c"];
const it = arr[Symbol.iterator]();
console.log(it.next()); // {value: "a", done: false}
console.log(it.next()); // {value: "b", done: false}
console.log(it.next()); // {value: "c", done: false}
console.log(it.next()); // {value: undefined, done: true}
// custom iterable
class Rentang {
constructor(awal, akhir) { this.awal = awal; this.akhir = akhir; }
*[Symbol.iterator]() {
for (let i = this.awal; i <= this.akhir; i++) yield i;
}
}
const r = new Rentang(1, 5);
console.log([...r]); // [1,2,3,4,5]
for (const n of r) console.log(n);Literal, properti, method, this, computed key.
Objek adalah kumpulan pasangan kunci-nilai: const user = { nama: "Ari", umur: 17 }. Akses via titik (user.nama) atau kurung siku (user["nama"]) — kurung siku menerima variabel dan string dinamis. Properti baru ditambah dengan assignment: user.email = "a@b.c"; dihapus dengan delete user.email.
Method adalah properti yang nilainya fungsi: sapa() { return "Halo " + this.nama }. this di dalam method merujuk objek pemanggil — TAPI this di arrow function mengambil this dari scope luar (lexical this), bukan objek. Ini perbedaan penting: gunakan method biasa untuk this; gunakan arrow untuk callback yang butuh this dari luar.
Computed key: { [kunci]: nilai } menggunakan nilai variabel sebagai nama properti. Shorthand: { nama, umur } bila nama variabel sama dengan properti.
this.nama di dalam method merujuk objek user. Computed key ["level_" + 1] menghitung nama properti dari ekspresi. Shorthand { nama, umur } setara { nama: nama, umur: umur }.
Uji: objek {nama:"Ari",umur:17} → cetak `Ari (17)`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
const user = {
nama: "Ari",
umur: 17,
sapa() { return `Halo ${this.nama}!`; },
["level_" + 1]: "gold", // computed key
};
console.log(user.sapa());
console.log(user["umur"]); // kurung siku
console.log(user.level_1); // gold
// shorthand
const nama = "Budi", umur = 20;
const singkat = { nama, umur };
console.log(singkat);Konversi objek↔array, assign, freeze, hasOwn.
Object.keys(obj) menghasilkan array nama properti; Object.values(obj) array nilai; Object.entries(obj) array pasangan [kunci, nilai]. Ketiganya adalah jalan antara objek dan array method — keys untuk iterasi, entries untuk map/filter/reduce pada objek.
Object.assign(target, ...sources) menggabung properti dari beberapa objek ke target. Spread { ...a, ...b } adalah versi modern yang tidak mengubah asli. Object.freeze(obj) membekukan — menambah/mengubah properti setelahnya diabaikan (strict mode: error).
Object.hasOwn(obj, "kunci") (ES2022) menggantikan hasOwnProperty — memeriksa apakah properti ADA di objek itu sendiri, bukan di prototype chain.
entries adalah jembatan ke array method: Object.entries(obj).filter(([k, v]) => v > 5) menyaring produk dengan stok di atas 5. Spread lebih ringkas dari assign untuk penggabungan.
Uji: Object.values({a:10,b:20,c:30}) → sum, harus `60`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
const stok = { kopi: 12, teh: 8, susu: 5 };
console.log(Object.keys(stok));
console.log(Object.values(stok));
console.log(Object.entries(stok));
// entries + destructuring
for (const [produk, jumlah] of Object.entries(stok)) {
console.log(`${produk}: ${jumlah}`);
}
// assign vs spread
const ekstra = { matcha: 3 };
const gabung1 = Object.assign({}, stok, ekstra);
const gabung2 = { ...stok, ...ekstra };
console.log(gabung2);
console.log(Object.hasOwn(stok, "kopi")); // true__proto__, Object.create, pewarisan berbasis prototype.
Setiap objek JavaScript punya prototype — objek lain tempat ia mencari properti yang tidak dimilikinya. Rantai pencarian ini disebut prototype chain: objek → prototype → prototype dari prototype → ... → null. Inilah pewarisan JavaScript: BUKAN class-based seperti Java, melainkan prototype-based.
Object.create(proto) membuat objek baru dengan prototype yang ditentukan: const anak = Object.create(induk). Properti yang tidak ditemukan di anak dicari di induk. __proto__ adalah properti internal untuk melihat/mengubah prototype (deprecated untuk produksi, tapi berguna untuk belajar).
Saat kamu menulis class di JavaScript, yang terjadi di balik layar tetap prototype chain — class adalah gula sintaksis di atas prototypal inheritance yang sudah ada.
anak tidak punya sapa — JavaScript naik ke induk dan menemukannya. grandChild naik dua tingkat: ke anak (nama) dan ke induk (sapa). Inilah prototype chain.
Uji: Object.create({x:42}) → akses .x, harus `42`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
const induk = { sapa() { return "Halo!"; } };
const anak = Object.create(induk);
anak.nama = "Ari";
console.log(anak.nama); // sendiri
console.log(anak.sapa()); // dari prototype
console.log(Object.getPrototypeOf(anak) === induk); // true
// prototype chain
grandChild = Object.create(anak);
console.log(grandChild.sapa()); // naik 2 tingkat ke induk
console.log(grandChild.nama); // naik 1 tingkat ke anakclass, constructor, method, static, getter/setter.
class Siswa { constructor(nama, poin) { this.nama = nama; this.poin = poin; } } — class membuat cetakan objek dengan constructor yang otomatis berjalan saat new Siswa(). Method dideklarasikan tanpa kata function: tambah(n) { this.poin += n; }.
Static method milik class itu sendiri, bukan instance: static bandingkan(a, b) dipanggil lewat Siswa.bandingkan(). Getter/setter: get poinTotal() { return this.poin * 10; } — dipanggil tanpa kurung seperti properti.
Private field (ES2022): #nama dengan tanda pagar — tidak bisa diakses dari luar class. Ini enforcement sungguhan (bukan konvensi underscore) — error bila diakses dari luar.
Getter status dipanggil tanpa kurung — seperti properti. Static bandingkan dipanggil dari class, bukan instance. #sandi benar-benar private — ari.#sandi dari luar error.
Uji: Siswa("Ari",17).info() harus `Ari (17)`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
class Siswa {
#sandi; // private field
constructor(nama, poin = 0) {
this.nama = nama;
this.poin = poin;
this.#sandi = "rahasia";
}
tambah(n) { this.poin += n; }
get status() {
return this.poin >= 70 ? "lulus" : "remedial";
}
set reset(v) { this.poin = v; }
static bandingkan(a, b) {
return a.poin > b.poin ? a.nama : b.nama;
}
}
const ari = new Siswa("Ari", 50);
ari.tambah(25);
console.log(ari.status); // lulus (dipanggil tanpa kurung!)
const budi = new Siswa("Budi", 80);
console.log(Siswa.bandingkan(ari, budi)); // BudiPewarisan class, super(), method override.
class Admin extends Pengguna { constructor(nama, email, level) { super(nama, email); this.level = level; } } — Admin mewarisi semua properti dan method Pengguna. super() memanggil constructor induk sebelum menambah inisialisasi sendiri.
Method override: deklarasikan method dengan nama sama di class anak. Panggil versi induk lewat super.methodName(). Ini memungkinkan menambah perilaku tanpa menulis ulang semuanya.
instanceof memeriksa pewarisan: admin instanceof Admin → true, admin instanceof Pengguna → true (karena Admin extends Pengguna). Multi-level: C extends B extends A — prototype chain tiga tingkat.
super(nama, email) mengisi bagian induk; info() dioverride menambah prefix [L5] sambil memanggil super.info() untuk bagian asli. instanceof mengenali seluruh rantai pewarisan.
Uji: Pelanggan extends Pengguna dengan saldo — harus `[C] Budi <b@m> 50000`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
class Pengguna {
constructor(nama, email) {
this.nama = nama;
this.email = email;
}
info() { return `${this.nama} <${this.email}>`; }
}
class Admin extends Pengguna {
constructor(nama, email, level) {
super(nama, email);
this.level = level;
}
info() {
return `[L${this.level}] ${super.info()}`;
}
}
const a = new Admin("Zaky", "ops@dev", 5);
console.log(a.info());
console.log(a instanceof Admin); // true
console.log(a instanceof Pengguna); // truePrivate #, static initializer, mixin pattern.
Private field (#nama) dan private method (#method()) benar-benar terkunci — hanya kode DI DALAM class yang bisa mengaksesnya. Ini berbeda dari konvensi _nama yang hanya "dihormati" tapi tidak dijalankan. Static block (class level initializer) berjalan sekali saat class didefinisikan: class Config { static { this.api = "..."; } }.
Mixin adalah pola menambah method ke class TANPA pewarisan: Object.assign(MyClass.prototype, mixinObject). Ini berguna bila butuh method dari beberapa sumber — JavaScript tidak mendukung multiple inheritance class, tapi mixin menggantikannya.
Instance vs static: instance method dipanggil dari objek (ari.tambah()), static dari class (Siswa.bandingkan()). Static tidak bisa mengakses this instance.
#riwayat dan #simpan benar-benar private — akses dari luar SyntaxError. Mixin Loggable menambahkan method log() ke Item.prototype tanpa pewarisan.
Uji: Counter dengan #count — increment() lalu read(), harus `1`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
class Kalkulator {
#riwayat = []; // private field
#simpan(operasi) { // private method
this.#riwayat.push(operasi);
}
tambah(a, b) {
const hasil = a + b;
this.#simpan(`tambah: ${hasil}`);
return hasil;
}
get log() { return this.#riwayat; }
}
const k = new Kalkulator();
k.tambah(3, 5);
k.tambah(10, 20);
console.log(k.log); // ["tambah: 8", "tambah: 30"]
// console.log(k.#riwayat); // SyntaxError!
// mixin
const Loggable = { log() { console.log(this.nama); } };
class Item { constructor(nama) { this.nama = nama; } }
Object.assign(Item.prototype, Loggable);
new Item("test").log();Map (kunci apa pun) dan Set (nilai unik).
Map adalah dictionary dengan kunci TIDAK TERBATAS string — objek, fungsi, NaN semuanya sah sebagai kunci. Set adalah koleksi nilai unik — duplikat otomatis dihilangkan. Keduanya iterable (for-of, spread) dan punya property size.
Map vs objek: Map menjaga urutan penyisipan, punya .size, mudah diiterasi, dan kunci tak dibebani prototype chain. Gunakan Map untuk data dinamis yang kuncinya berasal dari runtime; gunakan objek untuk struktur tetap.
WeakMap dan WeakSet tidak mencegah garbage collection — bila kunci tak lagi direferensikan di tempat lain, entri otomatis dihapus. Cocok untuk metadata yang menempel pada objek tanpa mencegah pembersihan memori.
Map menerima objek sebagai kunci — hal yang tidak bisa objek biasa (kunci objek selalu string). Set otomatis menghilangkan duplikat: [...new Set(arr)] adalah idiom deduplikasi satu baris.
Uji: new Set([1,2,2,3,3,3]).size — harus `3`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
const m = new Map();
m.set("nama", "Ari");
m.set(42, "angka sebagai kunci");
m.set({ id: 1 }, "objek sebagai kunci");
console.log(m.get("nama"));
console.log(m.size);
for (const [k, v] of m) console.log(k, "->", v);
const s = new Set([1, 2, 2, 3, 3, 3]);
console.log(s); // {1, 2, 3}
console.log(s.size); // 3
console.log(s.has(2)); // true
// deduplikasi array
const duplikat = [1, 2, 2, 3, 3];
console.log([...new Set(duplikat)]); // [1,2,3]Buat, format, dan bandingkan tanggal.
new Date() memberi waktu saat ini; new Date("2026-10-10") dari string; new Date(2026, 9, 10) dari komponen (bulan 0-indexed: 9 = Oktober!). Method: getFullYear(), getMonth() (0-11!), getDate(), getDay() (0=Minggu), getHours(), getMinutes(). Timestamp: Date.now() memberi milidetik sejak epoch (1 Jan 1970).
Date di JavaScript terkenal kurang ergonomis: getMonth() 0-indexed tapi getDate() 1-indexed; tidak ada built-in formatting yang bagus. toISOString() memberi format standar "2026-10-10T08:00:00.000Z"; toLocaleDateString("id-ID") memberi format lokal.
Selisih dua Date menghasilkan milidetik — bagi 1000 untuk detik, 86400000 untuk hari. Perbandingan: date1 < date2 bekerja karena Date dikonversi ke timestamp saat dibandingkan dengan < >.
getMonth() menghasilkan 0-11 — Oktober adalah 9, bukan 10. Selisih dua Date menghasilkan milidetik; bagi 86400000 untuk hari.
Uji: new Date(2026, 0, 15).getDate() — harus `15`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
const sekarang = new Date();
console.log(sekarang.getFullYear(), sekarang.getMonth() + 1);
const lahir = new Date(2009, 2, 15); // 15 Maret 2009 (bulan 2 = Maret!)
console.log(lahir.toLocaleDateString("id-ID"));
// timestamp
const mulai = Date.now();
// ... kerja berat ...
const selesai = Date.now();
console.log(`Durasi: ${selesai - mulai}ms`);
// selisih hari
const a = new Date("2026-01-01");
const b = new Date("2026-12-31");
console.log(Math.round((b - a) / 86400000)); // 364 hariKonversi objek↔string dengan replacer dan reviver.
JSON.stringify(obj) mengubah objek JavaScript menjadi string JSON; JSON.parse(teks) mengubah balik. Kombinasi ini juga cara cepat deep-clone sederhana: JSON.parse(JSON.stringify(obj)) — kehilangan function, undefined, symbol, dan Date berubah jadi string.
Replacer (parameter kedua stringify) memfilter atau mengubah: JSON.stringify(obj, ["nama","umur"]) hanya menyertakan properti tersebut. Atau fungsi: (kunci, nilai) => kunci === "sandi" ? "***" : nilai.
Reviver (parameter kedua parse) mengubah saat membaca: JSON.parse(teks, (k, v) => k === "tanggal" ? new Date(v) : v) mengembalikan Date object dari string ISO.
JSON.stringify dengan indent: stringify(obj, null, 2) menghasilkan format rapi dengan indentasi 2 spasi.
Replacer (k, v) => mengganti sandi dengan "***" saat serialisasi. Reviver mengubah string tanggal kembali menjadi Date object saat parse. Indent 2 menghasilkan JSON terbaca manusia.
Uji: stringify {nama:"Ari",umur:17} → parse → .umur, harus `17`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
const data = {
nama: "Ari",
umur: 17,
sandi: "rahasia",
hobi: ["coding", "game"],
};
// stringify dengan replacer (sensor sandi)
const teks = JSON.stringify(data, (k, v) =>
k === "sandi" ? "***" : v
);
console.log(teks);
// parse dengan reviver
const balik = JSON.parse('{"nama":"Budi","tanggal":"2026-01-15"}',
(k, v) => k === "tanggal" ? new Date(v) : v
);
console.log(balik.tanggal.getFullYear()); // 2026
// format rapi
console.log(JSON.stringify(data, null, 2));Literal, index, push/pop, splice, slice, length.
Array adalah daftar terurat: const buah = ["apel", "mangga", "jeruk"]. Indeks mulai nol; buah[0] adalah "apel". length adalah properti (bukan method). push menambah di akhir; pop mengambil-hapus terakhir; unshift menambah di awal; shift mengambil-hapus pertama.
splice(start, deleteCount, ...items) adalah pisau serbaguna: menghapus, menyisip, dan mengganti sekaligus — ia MENGGUBAH array asli. slice(a, b) mengambil bagian TANPA mengubah asli (sama seperti string). indexOf mencari posisi; includes menguji keanggotaan.
Array bisa berisi tipe campuran dan bersarang. Array adalah REFERENCE — b = a tidak menyalin; gunakan [...a] untuk salinan dangkal atau JSON.parse(JSON.stringify(a)) untuk salinan dalam.
splice(2, 1, 99) menghapus satu elemen di indeks 2 lalu menyisipkan 99 — array asli berubah. slice(1,3) menyalin elemen 1-2 tanpa mengubah.
Uji: sort [5,3,8,1,9] — harus `[1,3,5,8,9]`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
const arr = [1, 2, 3, 4, 5];
arr.push(6); // [1,2,3,4,5,6]
arr.pop(); // [1,2,3,4,5]
arr.unshift(0); // [0,1,2,3,4,5]
arr.shift(); // [1,2,3,4,5]
arr.splice(2, 1, 99); // hapus indeks 2, sisip 99
console.log(arr); // [1,2,99,4,5]
console.log(arr.slice(1, 3)); // [2,99]
console.log(arr.includes(99)); // trueSort comparator, flat, at, findLast, Array.from.
sort() default mengurutkan sebagai STRING — [10, 2, 1].sort() menghasilkan [1, 10, 2]! Untuk angka: sort((a, b) => a - b). Unturan objek: sort((a, b) => a.poin - b.poin). reverse() membalik di tempat.
flat(depth) meratakan array bersarang: [[1,2],[3,[4,5]]].flat(2) memberi [1,2,3,4,5]. flatMap = map + flat(1). at(index) menerima indeks negatif: arr.at(-1) memberi elemen terakhir (ES2022).
Array.from(iterable, mapFn) mengubah iterable atau array-like menjadi array sungguhan: Array.from("halo") memberi ["h","a","l","o"]. Array.of(1,2,3) membuat array dari argumen — lebih aman dari new Array(3) yang membuat array kosong panjang 3, bukan [3].
sort() default mengurutkan string — [10,2].sort() memberi [10,2] karena "1"<"2" secara string. Selalu sediakan comparator untuk angka. at(-1) menggantikan arr[arr.length-1].
Uji: sort siswa by poin desc — siswa[0].n harus `Budi`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
// sort numerik
const angka = [10, 2, 1, 33, 5];
console.log(angka.sort()); // [1,10,2,33,5] — string!
console.log([...angka].sort((a, b) => a - b)); // [1,2,5,10,33]
// sort objek
const siswa = [{ n: "Budi", p: 92 }, { n: "Ari", p: 85 }];
siswa.sort((a, b) => b.p - a.p);
console.log(siswa[0].n); // Budi
// flat
console.log([[1, 2], [3, [4, 5]]].flat(2)); // [1,2,3,4,5]
// at
const d = [10, 20, 30];
console.log(d.at(-1)); // 30
// Array.from
console.log(Array.from("halo")); // ["h","a","l","o"]Konversi iterable/array-like ke array + generator ke array.
Array.from() mengubah DUA jenis sumber menjadi array: iterable (Map, Set, generator, string) dan array-like (arguments, NodeList) — objek punya .length dan indeks numerik tapi bukan array sungguhan. Versi dengan mapFn: Array.from(sumber, x => x * 2) mengkonversi sekaligus mentransformasi.
Spread [...] juga mengubah iterable ke array, tapi HANYA yang iterable — arguments dan NodeList butuh Array.from. Keduanya menghasilkan array baru (salinan), bukan reference.
Generator + spread = array instan: function* range(n) { for (let i=0; i<n; i++) yield i; } lalu [...range(5)] memberi [0,1,2,3,4]. Ini pola yang bersih untuk membuat deret tanpa loop manual.
Array.from dengan mapFn mengkonversi dan mentransformasi sekaligus — kuadrat dari range(5). Spread dari Set otomatis deduplikasi.
Uji: generator range(5) → [...range(5)] harus `[0,1,2,3,4]`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
// dari string
console.log(Array.from("halo"));
// dari Map
const m = new Map([["a", 1], ["b", 2]]);
console.log([...m]); // [["a",1],["b",2]]
// dari Set
console.log([...new Set([1, 2, 2, 3])]); // [1,2,3]
// dari generator
function* range(n) {
for (let i = 0; i < n; i++) yield i;
}
console.log([...range(5)]); // [0,1,2,3,4]
console.log(Array.from(range(5), x => x * x)); // [0,1,4,9,16]Literal, test, exec, match, pola umum.
RegExp ditulis /pola/flags: /d+/g, /[a-z]+/i, /^halo$/. Method pada string: match menghasilkan array kecocokan, replace mengganti, search memberi posisi, split memecah. Method pada RegExp: test menghasilkan boolean, exec memberi detail kecocokan pertama.
Pola dasar: d digit, w word char, s whitespace, . sembarang. Kuantifier: + satu atau lebih, * nol atau lebih, ? opsional, {3} tepat 3, {2,5} 2-5. Anchor: ^ awal, $ akhir. Kelas: [abc] salah satu, [^abc] selain itu. Escape: . titik literal, \ backslash.
Flags: g (global — semua kecocokan, bukan hanya pertama), i (case-insensitive), m (multiline), s (dotall — . mencakup newline), u (unicode), y (sticky).
match dengan flag g menghasilkan array semua kecocokan. exec memberi grup penangkap: hasil[1] dan hasil[2] adalah bagian dalam kurung.
Uji: "harga 25000".match(/d+/)[0] — harus `25000`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
const teks = "Kontak: andi@kampus.com, budi@kantor.com, 08123456789";
// test: ada tidak
console.log(/d+/.test(teks)); // true
// match: semua angka
console.log(teks.match(/d+/g));
// match: email (sederhana)
console.log(teks.match(/w+@w+.w+/g));
// replace dengan flag g
console.log(teks.replace(/d+/g, "XXX"));
// exec: detail kecocokan
const pola = /(w+)@(w+)/;
const hasil = pola.exec(teks);
console.log(hasil[0], hasil[1], hasil[2]); // andi@kampus andi kampusNamed group, lookbehind, replace dengan fungsi.
Named group (?<nama>...) memberi nama pada grup penangkap: /(?<tahun>d{4})-(?<bulan>d{2})/ lalu hasil.groups.tahun. Ini jauh lebih terbaca daripada indeks [1], [2].
Lookahead (?=...) dan negative lookahead (?!...) memeriksa tanpa mengkonsumsi: /d+(?=rp)/ angka diikuti "rp" tanpa mengambil "rp". Lookbehind (?<=...) dan (?<!...) memeriksa sebelum posisi saat ini.
replace dengan fungsi: teks.replace(/(w+)/g, (match, kata) => kata.toUpperCase()) — fungsi menerima tiap kecocokan dan mengembalikan penggantinya. Ini lebih fleksibel dari string pengganti statis.
Named group mengakses bagian pola lewat nama (m.groups.tahun). Replace dengan fungsi mengubah tiap kata menjadi kapital awal. Lookbehind mengambil angka SETELAH "Rp" tanpa menyertakan "Rp".
Uji: "Rp15000".match(/(?<=Rp)d+/) — harus `15000`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
// named group
const tanggal = "2026-10-10";
const m = tanggal.match(/(?<tahun>d{4})-(?<bulan>d{2})-(?<hari>d{2})/);
console.log(m.groups.tahun, m.groups.bulan); // 2026 10
// replace dengan fungsi
const teks = "halo dunia belajar javascript";
const kapital = teks.replace(/w+/g, kata =>
kata[0].toUpperCase() + kata.slice(1)
);
console.log(kapital); // Halo Dunia Belajar Javascript
// lookbehind
console.log("Rp1000 Rp2000".match(/(?<=Rp)d+/g)); // ["1000","2000"]setTimeout, callback, event loop — kenapa async ada.
JavaScript adalah single-threaded — satu tugas pada satu waktu. Bila tugas butuh 5 detik (misal ambil data dari server), seluruh halaman membeku. Asynchronous memecahkan ini: tugas lambat "diparkir", JavaScript lanjut, dan hasil diambil lewat callback saat siap.
setTimeout(fn, ms) menjadwalkan fn setelah ms milidetik — paling dasar dari async. Callback adalah fungsi yang dipanggil "nanti": saat timer selesai, saat data tiba, saat pengguna klik. Callback hell terjadi saat callback bersarang terlalu dalam — Promise datang menyelesaikannya.
Event loop adalah mesin di baliknya: call stack mengeksekusi sinkron, callback queue menunggu, dan event loop memindahkan callback ke stack saat stack kosong. Ini kenapa setTimeout(fn, 0) TIDAK berjalan seketika — ia menunggu stack kosong.
Urutan keluaran menunjukkan event loop: kode sinkron (1, 2) selesai dulu, baru timer diambil dari queue (3, 4). setTimeout 0 menunggu stack kosong, bukan nol milidetik literal.
Uji: setTimeout order — `sekarang` lalu `nanti`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
console.log("1: mulai");
setTimeout(() => console.log("3: timer 0ms"), 0);
setTimeout(() => console.log("4: timer 100ms"), 100);
console.log("2: sinkron selesai");
// Urutan keluaran: 1, 2, 3, 4
// setTimeout 0 TIDAK berjalan seketika — menunggu stack kosong.Chaining, error handling, Promise.all/race/allSettled.
Promise adalah objek yang mewakili nilai yang BELUM tersedia tapi akan tiba: new Promise((resolve, reject) => { ... }). resolve(nilai) memenuhi janji; reject(error) menolaknya. Konsumennya: .then(hasil), .catch(error), .finally(selalu).
Chaining: .then mengembalikan Promise baru sehingga bisa dirantai: fetchData().then(d => proses(d)).then(h => simpan(h)).catch(e => log(e)).finally(() => cleanup()). Satu catch di akhir menangkap error dari seluruh rantai.
Promise.all([p1, p2, p3]) menunggu SEMUA selesai — hasil array berurutan; satu gagal, semuanya gagal. Promise.race memberi yang PERTAMA selesai (sukses atau gagal). Promise.allSettled menunggu semua selesai APA PUN hasilnya — array {status, value/reason}. Promise.any memberi yang pertama BERHASIL.
Rantai then mengalirkan hasil: resolve({nama,umur}) → then pertama mengubah jadi string → then kedua mencetak. Promise.all menunggu dua resolve lalu mendestruksi hasilnya [a, b].
Uji: Promise.all([resolve(10), resolve(20)]) → a+b, harus `30`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
// Promise dasar
const ambilData = new Promise((resolve, reject) => {
const sukses = true;
if (sukses) resolve({ nama: "Ari", umur: 17 });
else reject(new Error("gagal mengambil"));
});
ambilData
.then(d => `${d.nama} (${d.umur})`)
.then(teks => console.log(teks))
.catch(e => console.error(e.message));
// Promise.all
const p1 = Promise.resolve(10);
const p2 = Promise.resolve(20);
Promise.all([p1, p2]).then(([a, b]) => console.log(a + b)); // 30Async function, await, try/catch untuk error.
async function adalah deklarasi yang SELALU mengembalikan Promise. await menjeda eksekusi async function sampai Promise selesai, lalu memberi nilainya — kode async terbaca seperti sinkron.
Error handling: bungkus await dengan try/catch — reject dari Promise berubah menjadi exception yang bisa ditangkap biasa. Ini jauh lebih bersih daripada .catch berantai.
async function yang berisi await lain TIDAK memblokir — hanya fungsi ITU yang menunggu; program lain lanjut. Untuk menjalankan beberapa async secara PARALEL (bukan berurutan), gunakan Promise.all([f1(), f2()]) lalu await hasilnya sekaligus.
await berurutan menunggu 200ms total (A lalu B); Promise.all menunggu 100ms saja karena X dan Y berjalan bersamaan. Gunakan Promise.all untuk paralel, await biasa untuk berurutan.
Uji: async + await Promise.all → jumlah, harus `30`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
function buatPromise(ms, nilai) {
return new Promise(resolve => setTimeout(() => resolve(nilai), ms));
}
async function main() {
// berurutan
const a = await buatPromise(100, "A");
const b = await buatPromise(100, "B");
console.log(a, b); // A B (total 200ms)
// paralel
const [x, y] = await Promise.all([
buatPromise(100, "X"),
buatPromise(100, "Y"),
]);
console.log(x, y); // X Y (total 100ms)
}
main();fetch, Response, JSON, POST dengan body.
fetch(url) mengembalikan Promise yang resolve ke Response object. Response punya .ok (boolean), .status (kode HTTP), .json() (Promise hasil parse), .text() (Promise string). fetch TIDAK melempar error pada 404/500 — ia resolve dengan response.ok false; kamu harus memeriksa manual.
POST dengan body: fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }). Response juga bisa dibaca lewat .text() atau .blob() untuk file.
Fetch adalah pengganti XMLHttpRequest — API yang lebih bersih berbasis Promise. Digunakan untuk mengambil data dari API, mengirim form, dan berkomunikasi dengan server.
resp.ok memeriksa sukses HTTP (200-299). resp.json() juga Promise — perlu await kedua. POST mengirim body sebagai string JSON dengan header Content-Type.
Uji: tulis pola fetch GET dengan response.ok check.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
// GET
async function ambilUser() {
const resp = await fetch("https://httpbin.org/get");
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
const data = await resp.json();
return data;
}
// POST
async function kirimUser(user) {
const resp = await fetch("https://httpbin.org/post", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(user),
});
return resp.json();
}
console.log("Pola fetch siap — jalankan di browser atau Node 18+.");Named export, default export, dynamic import.
ES Modules memecah kode ke berkas-berkas: export dari satu sisi, import di sisi lain. Named export: export const nama = "Ari" diimpor dengan import { nama } from "./berkas.js". Default export: export default function main() diimpor dengan nama bebas: import apapun from "./berkas.js". Satu berkas hanya punya SATU default tapi banyak named.
Import rename: import { nama as n } — berganti nama lokal. Import semua: import * as util from "./util.js" lalu util.fungsi(). Dynamic import: const modul = await import("./besar.js") — hanya dimuat saat dibutuhkan, menghasilkan Promise.
Modules punya scope sendiri — variabel tidak bocor ke global. Ini perbaikan besar dari IIFE dan script tag kaku. Di browser pakai <script type="module">; di Node.js gunakan .mjs atau "type": "module" di package.json.
Default import (sapa) tanpa kurung kurawal, named import ({PI, luas}) dengan. as mengganti nama. * as util mengemas semua named ke satu objek.
Uji: default export function yang mengembalikan `Halo dari module!`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
// ---- util.js ----
export const PI = 3.14159;
export function luas(r) { return PI * r * r; }
export default function sapa() { return "Halo!"; }
// ---- main.js ----
import sapa, { PI, luas } from "./util.js";
import { PI as phi } from "./util.js";
import * as util from "./util.js";
console.log(sapa(), PI, luas(7));
console.log(phi, util.luas(14));
// dynamic import
// const modul = await import("./util.js");Intersep operasi objek + API refleksi terpadu.
Proxy membungkus objek dan mencegat operasi padanya: get (baca properti), set (tulis properti), has (in operator), deleteProperty, dan lainnya. Ini fondasi framework reactivity (Vue 3, MobX) dan validasi otomatis.
const p = new Proxy(target, handler) — handler berisi trap: get(target, prop) { ... return Reflect.get(target, prop) }. Reflect menyediakan method yang sama dengan Proxy trap — gunakan keduanya bersama.
Symbol.iterator sebagai generator method membuat objek custom bisa di-for-of dan di-spread. Symbol.toPrimitive mengatur konversi objek ke primitif (number/string). Well-known symbols ini adalah pintu ke metaprogramming tingkat lanjut.
Trap get mencegat SETIAP pembacaan properti; set mencegat penulisan. Reflect.get/set melakukan operasi asli setelah logging. Ini cara framework melacak perubahan data secara otomatis.
Uji: Proxy yang uppercase setiap get — proxy.nama harus `ARI`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
// Proxy: log semua akses
const data = { nama: "Ari", umur: 17 };
const proxy = new Proxy(data, {
get(target, prop) {
console.log(`baca: ${prop}`);
return Reflect.get(target, prop);
},
set(target, prop, nilai) {
console.log(`tulis: ${prop} = ${nilai}`);
return Reflect.set(target, prop, nilai);
},
});
console.log(proxy.nama); // log: "baca: nama" lalu "Ari"
proxy.umur = 18; // log: "tulis: umur = 18"console.table, debugger, kesalahan klasik JavaScript.
Debugging di JavaScript: console.log untuk inspeksi cepat, console.table(arr) untuk array objek rapi, console.trace() untuk jejak stack. Statement debugger menjeda eksekusi di DevTools — set breakpoint tanpa klik. Error stack trace menunjukkan urutan pemanggilan yang mengarah ke error.
Kesalahan klasik: (1) == vs === — selalu gunakan ===; (2) this dalam arrow vs method biasa; (3) 0.1 + 0.2 !== 0.3 (floating-point); (4) sort() default string; (5) var hoisting dan function scope; (6) mutation tak sengaja — b = a membuat reference bukan salinan; (7) NaN !== NaN.
Kebiasaan sehat: gunakan const secara default; selalu periksa response.ok pada fetch; tangani Promise rejection dengan .catch atau try/catch; test dengan console.log sebelum refactor.
console.table mencetak array objek sebagai tabel rapi di console. Perhatikan == vs ===, sort default, dan reference vs salinan — tiga jebakan yang paling sering menangkap pemula.
Uji: buktikan [10,2].sort() memberi `[10,2,` bukan `[2,10]` (string sort).
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
// console.table untuk array objek
const siswa = [
{ nama: "Ari", poin: 85 },
{ nama: "Budi", poin: 92 },
];
console.table(siswa);
// kesalahan klasik
console.log("5" == 5); // true — coercion!
console.log("5" === 5); // false — strict
console.log([10, 2].sort()); // [10, 2] — string!
console.log([10, 2].sort((a,b)=>a-b)); // [2, 10] — number
// reference
const a = [1, 2];
const b = a; // reference
const c = [...a]; // salinan
b.push(3);
console.log(a === b); // true — sama!
console.log(a === c); // false — bedaBOM: window.alert, document.title, navigator, location.
Di browser, objek window adalah kontainer global — semua variabel global dan fungsi built-in adalah propertinya. window.alert() menampilkan popup, window.confirm() memberi OK/Cancel, window.prompt() meminta input. document adalah window.document — pintu ke seluruh halaman HTML.
Objek penting di BOM (Browser Object Model): navigator menyimpan info browser (navigator.userAgent, navigator.language), location menyimpan dan mengatur URL (location.href, location.reload()), history untuk riwayat (history.back()), screen untuk resolusi layar.
Karena window adalah global, menulis alert("halo") sama dengan window.alert("halo") — kata window boleh dihilangkan. document.title membaca/mengubah judul tab; document.URL memberi alamat lengkap.
document.title membaca lalu mengubah judul tab browser. navigator.language memberi bahasa browser. Semua ini diakses tanpa kata window karena ia global.
Uji: cetak `judul tab siap diubah` lalu `bahaya: id-ID`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
<!doctype html>
<html>
<head><title>Belajar BOM</title></head>
<body>
<button onclick="alert('Halo!')">Klik aku</button>
<script>
console.log(document.title); // "Belajar BOM"
console.log(navigator.language); // "id-ID"
console.log(location.href); // URL lengkap
document.title = "Judul baru!"; // mengubah judul tab
</script>
</body>
</html>getElementById, querySelector, querySelectorAll.
DOM (Document Object Model) mengubah HTML menjadi pohon objek JavaScript yang bisa dimanipulasi. Untuk mengubah elemen, pertama pilih elemennya: document.getElementById("id") memberi SATU elemen ber-id tersebut. document.querySelector("css-selector") memberi elemen PERTAMA yang cocok dengan selector CSS apa pun — ".kelas", "#id", "nav > ul > li".
querySelectorAll memberi NodeList berisi SEMUA elemen yang cocok — bisa di-for-each. NodeList mirip array tapi bukan array sungguhan; konversi dengan [...list] untuk method array lengkap.
Modern praktik: selalu gunakan querySelector/querySelectorAll — mereka menerima selector CSS lengkap sehingga satu API menutup semua kebutuhan. getElementById lebih cepat tapi terbatas pada id saja.
querySelector(".teks") memberi elemen PERTAMA berkelas teks; querySelectorAll memberi NodeList dua elemen. forEach bekerja langsung di NodeList tanpa konversi.
Uji: buat fungsi pilih(data, selector) yang mengembalikan elemen ber-id — pilih([{id:"a"},{id:"b"}], "b") harus `b`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
<!doctype html>
<html>
<body>
<h1 id="judul">Halo</h1>
<p class="teks">Paragraf 1</p>
<p class="teks">Paragraf 2</p>
<script>
// cara klasik
const j = document.getElementById("judul");
// cara modern
const p1 = document.querySelector(".teks");
const semua = document.querySelectorAll(".teks");
console.log(j.textContent); // Halo
console.log(p1.textContent); // Paragraf 1
console.log(semua.length); // 2
semua.forEach(p => console.log(p.textContent));
</script>
</body>
</html>Buat, sisip, hapus elemen secara dinamis.
Membuat elemen baru: const li = document.createElement("li"); lalu isi: li.textContent = "Item baru"; lalu sisip: parent.appendChild(li) menambah di akhir atau parent.prepend(li) di awal. insertBefore(li, referensi) menyisip sebelum elemen tertentu.
innerHTML membaca atau mengganti seluruh HTML dalam elemen: div.innerHTML = "<b>tebal</b> <i>miring</i>". CEPAT tapi berisiko XSS bila konten berasal dari pengguna. textContent AMAN — semua teks dirender sebagai teks biasa, tag tidak ditafsirkan.
remove() menghapus elemen dari DOM: element.remove(). replaceChild(new, old) mengganti elemen. classList.add/remove/toggle mengatur kelas CSS tanpa menyentuh string className langsung.
Loop membuat tiga li lalu appendChild menambah di akhir. prepend menyisip di awal. remove() pada lastElementChild menghapus yang terakhir — hasil akhir tiga item.
Uji: fungsi buatList(items) mengembalikan array string `<li>` — 3 item harus `['<li>Item 1</li>','<li>Item 2</li>','<li>Item 3</li>']`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
<!doctype html>
<html>
<body>
<ul id="daftar"></ul>
<script>
const ul = document.getElementById("daftar");
// buat dan sisip
for (let i = 1; i <= 3; i++) {
const li = document.createElement("li");
li.textContent = `Item ${i}`;
li.classList.add("item");
ul.appendChild(li);
}
// prepend di awal
const pertama = document.createElement("li");
pertama.textContent = "Paling depan";
ul.prepend(pertama);
// hapus terakhir
ul.lastElementChild.remove();
console.log(ul.children.length); // 3
</script>
</body>
</html>Ubah CSS dari JavaScript: kelas, inline style, custom property.
Dua cara mengubah tampilan dari JavaScript. Pertama: element.style.properti = nilai — inline style pada elemen itu. style.backgroundColor, style.fontSize, style.display. Nama properti CSS berubah dari kebab-case ke camelCase (background-color → backgroundColor).
Kedua (lebih baik): element.classList.add("kelas"), .remove, .toggle (hidup-mati), .contains (periksa). Menambah/menghapus kelas CSS lebih terstruktur — gaya didefinisikan di CSS, JavaScript hanya mengaktifkan/menonaktifkan.
CSS custom property: element.style.setProperty("--warna", "teal") mengubah variabel CSS dari JS — sangat berguna untuk tema dan animasi. getComputedStyle(el) membaca gaya final setelah CSS diterapkan.
style.color mengubah warna teks langsung. classList.add("highlight") menambah kelas CSS yang mendefinisikan background kuning. toggle membalik keadaan — tambah bila belum ada, hapus bila sudah.
Uji: fungsi toggle(el, kelas) — panggil 2x, hasil kedua harus `false` (toggle off).
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
<!doctype html>
<html>
<head>
<style>
.highlight { background: yellow; font-weight: bold; }
.hidden { display: none; }
</style>
</head>
<body>
<p id="teks">Halo dunia</p>
<script>
const p = document.getElementById("teks");
// inline style
p.style.color = "blue";
p.style.fontSize = "1.2em";
// classList
p.classList.add("highlight");
console.log(p.classList.contains("highlight")); // true
// toggle: hidup-mati
p.classList.toggle("highlight");
console.log(p.classList.contains("highlight")); // false
</script>
</body>
</html>Klik, mouseover, keyboard — interaktivitas dimulai di sini.
Event adalah jantung interaktivitas web: klik, ketik, scroll, hover — semuanya event. addEventListener("eventType", handler) mendaftarkan fungsi yang berjalan saat event terjadi: button.addEventListener("click", () => alert("diklik!")).
Event yang sering: click, dblclick, mouseover, mouseout, keydown (saat tombol ditekan), keyup (saat dilepas), input (saat nilai form berubah), change (saat selesai berubah), submit (form), scroll, focus, blur.
Handler menerima event object berisi detail: e.target (elemen yang memicu), e.key (tombol keyboard yang ditekan), e.clientX/clientY (posisi mouse), e.preventDefault() (mencegah perilaku bawaan seperti submit form).
Klik tombol membaca input.value dan menampilkan sapaan. Event "input" menyala pada SETIAP ketikan. keydown Enter memicu btn.click() — keyboard shortcut sederhana.
Uji: buat eventBus sederhana — on("klik", fn) lalu emit("klik") menjalankan fn.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
<!doctype html>
<html>
<body>
<button id="btn">Klik aku</button>
<input id="nama" placeholder="Ketik nama...">
<p id="hasil"></p>
<script>
const btn = document.getElementById("btn");
const input = document.getElementById("nama");
const hasil = document.getElementById("hasil");
btn.addEventListener("click", () => {
hasil.textContent = `Halo, ${input.value || "Tamu"}!`;
});
input.addEventListener("input", (e) => {
console.log("mengetik:", e.target.value);
});
document.addEventListener("keydown", (e) => {
if (e.key === "Enter") btn.click();
});
</script>
</body>
</html>target, preventDefault, stopPropagation, event delegation.
Event object (biasanya bernama e atau event) membawa detail kejadian: e.target adalah elemen yang MEMICU event (bukan elemen yang mendaftar), e.currentTarget adalah elemen yang mendaftar. e.type memberi nama event. e.preventDefault() mencegah aksi bawaan — form tidak submit, link tidak pindah halaman.
e.stopPropagation() mencegah event menggelembung (bubble) ke elemen induk. Tanpanya, klik pada tombol di dalam form juga memicu event click pada form itu sendiri (event bubbling).
Event delegation memanfaatkan bubbling: pasang listener di INDUK untuk menangani semua anak. ul.addEventListener("click", e => { if (e.target.tagName === "LI") ... }) — satu listener untuk semua li, termasuk yang ditambahkan nanti. Ini pola yang lebih efisien daripada listener per elemen.
Satu listener di UL menangani klik SEMUA li — termasuk "Bantuan" yang ditambahkan SETELAH listener terpasang. Ini kekuatan delegation: tidak perlu listener per elemen.
Uji: delegation — klik li menampilkan textContent-nya; gunakan e.target.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
<!doctype html>
<html>
<body>
<ul id="menu">
<li>Beranda</li>
<li>Tentang</li>
<li>Kontak</li>
</ul>
<script>
// delegation: satu listener untuk semua li
const menu = document.getElementById("menu");
menu.addEventListener("click", (e) => {
if (e.target.tagName === "LI") {
console.log(`Klik: ${e.target.textContent}`);
e.target.classList.toggle("aktif");
}
});
// tambah li baru — delegation otomatis menangani
const li = document.createElement("li");
li.textContent = "Bantuan";
menu.appendChild(li);
</script>
</body>
</html>Input, select, textarea, validasi, submit event.
Form adalah cara pengguna memasukkan data. Tiap input punya .value untuk membaca/menulis nilai. input.value string; checkbox.checked boolean; select.value nilai option yang dipilih. e.preventDefault() pada submit mencegah halaman reload — tangani data di JavaScript.
Validasi sederhana: periksa input.value.trim() !== "" untuk wajib isi; input.value.length untuk panjang minimum; regex untuk format email. Tambahkan pesan error di elemen span di samping input. HTML5 validasi bawaan: atribut required, minlength, pattern — browser memeriksa otomatis; form.checkValidity() memberi boolean.
Untuk mengambil seluruh data form sekaligus: new FormData(form) memberi iterator pasangan [nama, nilai] dari semua input bernama.
preventDefault mencegah halaman reload saat submit. FormData mengambil semua input bernama sekaligus. Validasi manual memeriksa panjang nama sebelum menerima.
Uji: validasi nama minimal 2 karakter dan email mengandung @ — validasi("A","a@b") harus `nama terlalu pendek`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
<!doctype html>
<html>
<body>
<form id="daftar">
<input name="nama" placeholder="Nama" required>
<input name="email" type="email" placeholder="Email" required>
<button type="submit">Daftar</button>
</form>
<p id="pesan"></p>
<script>
const form = document.getElementById("daftar");
const pesan = document.getElementById("pesan");
form.addEventListener("submit", (e) => {
e.preventDefault(); // jangan reload!
const data = new FormData(form);
const nama = data.get("nama");
const email = data.get("email");
if (nama.trim().length < 2) {
pesan.textContent = "Nama terlalu pendek.";
return;
}
pesan.textContent = `Terdaftar: ${nama} <${email}>`;
form.reset();
});
</script>
</body>
</html>Navigasi pohon DOM: parentElement, children, nextElementSibling.
DOM adalah pohon — setelah memilih satu elemen, kamu bisa berjalan ke atas, bawah, dan samping. parentElement memberi induk; children memberi koleksi anak (HTMLCollection, bukan array); firstElementChild dan lastElementChild memberi anak pertama/terakhir.
Berjalan horizontal: nextElementSibling dan previousElementSibling memberi tetangga kanan-kiri pada level yang sama. closest("selector") naik ke atas mencari induk TERDEKAT yang cocok dengan selector — sangat berguna untuk mencari container dari tombol yang diklik.
Perbedaan penting: children hanya element node (disarankan); childNodes termasuk text node (spasi dan newline antar tag). Gunakan versi "Element" untuk navigasi yang bersih.
closest(".kartu") naik dari tombol ke div induk berkelas kartu. previousElementSibling memberi p sebelum tombol. Tombol menghapus seluruh kartu lewat kartu.remove().
Uji: fungsi cariInduk(node, kelas) — naik parentElement sampai ketemu kelas.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
<!doctype html>
<html>
<body>
<div class="kartu">
<h3>Judul Kartu</h3>
<p>Isi kartu...</p>
<button class="hapus">Hapus</button>
</div>
<script>
const btn = document.querySelector(".hapus");
// naik ke kartu induk
const kartu = btn.closest(".kartu");
console.log(kartu.querySelector("h3").textContent); // Judul Kartu
// navigasi
console.log(kartu.parentElement.tagName); // BODY
console.log(kartu.children.length); // 3 (h3, p, button)
console.log(btn.previousElementSibling.textContent); // Isi kartu...
// hapus kartu dari tombol di dalamnya
btn.addEventListener("click", () => kartu.remove());
</script>
</body>
</html>Simpan data di browser — bertahan setelah refresh.
localStorage menyimpan data di browser yang BERTAHAN setelah tab ditutup dan browser di-restart — sempurna untuk preferensi pengguna, tema, dan keranjang belanja. sessionStorage sama tapi hilang saat tab ditutup — cocok untuk data sementara seperti form yang belum disubmit.
API: localStorage.setItem("kunci", nilai), getItem("kunci"), removeItem("kunci"), clear(). Nilai SELALU string — objek perlu JSON.stringify untuk menyimpan dan JSON.parse untuk membaca. Key dapat diiterasi lewat Object.keys(localStorage).
Jumlah penyimpanan: sekitar 5-10MB per origin — cukup untuk kebutuhan ringan. Untuk data besar atau offline penuh, gunakan IndexedDB (database di browser).
Objek disimpan sebagai string JSON — stringify untuk menyimpan, parse untuk membaca. getItem mengembalikan null bila kunci tidak ada (bukan error).
Uji: simulasi localStorage — set/get/remove dengan Map.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
<!doctype html>
<html>
<body>
<script>
// simpan string
localStorage.setItem("nama", "Ari");
// simpan objek (harus stringify)
const user = { nama: "Budi", umur: 20 };
localStorage.setItem("user", JSON.stringify(user));
// baca
console.log(localStorage.getItem("nama")); // "Ari"
const balik = JSON.parse(localStorage.getItem("user"));
console.log(balik.nama); // "Budi"
// hapus
localStorage.removeItem("nama");
console.log(localStorage.getItem("nama")); // null
</script>
</body>
</html>Ambil data dari API tanpa reload + update DOM.
AJAX (Asynchronous JavaScript and XML — sekarang JSON) memungkinkan halaman mengambil data dari server TANPA reload. fetch() mengembalikan Promise; await memberi Response; response.json() memberi data. Setelah data tiba, update DOM dengan createElement/innerHTML.
Pola lengkapnya: (1) fetch ke API endpoint; (2) periksa response.ok; (3) parse JSON; (4) iterasi hasil dan bangun elemen DOM; (5) sisip ke container. Bungkus dengan async/await + try/catch untuk error handling.
CORS (Cross-Origin Resource Sharing) adalah kebijakan keamanan browser: fetch ke domain BERBEDA diblokir kecuali server mengizinkan lewat header Access-Control-Allow-Origin. Gunakan API publik yang sudah mengizinkan CORS, atau proxy server sendiri.
fetch mengambil data dari API publik, json() mem-parsing, forEach membangun li untuk tiap user lalu appendChild menyisip. try/catch menangkap network error dan HTTP error.
Uji: simulasi fetch dengan Promise yang resolve data — tampilkan nama pertama.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
<!doctype html>
<html>
<body>
<ul id="hasil"></ul>
<script>
async function muatUser() {
try {
const resp = await fetch("https://jsonplaceholder.typicode.com/users");
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
const users = await resp.json();
const ul = document.getElementById("hasil");
users.slice(0, 5).forEach(u => {
const li = document.createElement("li");
li.textContent = `${u.name} — ${u.email}`;
ul.appendChild(li);
});
} catch (e) {
console.error("Gagal:", e.message);
}
}
muatUser();
</script>
</body>
</html>Trigger CSS transition dari JS + requestAnimationFrame.
Cara termudah menganimasikan: trigger CSS transition dari JavaScript. Definisikan transition di CSS (.kotak { transition: transform 0.3s ease; }) lalu ubah properti dari JS (kotak.style.transform = "translateX(100px)"). Browser menghaluskan perpindahan secara otomatis.
requestAnimationFrame(fn) menjadwalkan fn sebelum frame berikutnya dipaint — sinkron dengan refresh rate layar (60fps). Gunakan untuk animasi yang butuh kontrol per-frame: permainan, canvas, parallax. rAF lebih efisien daripada setInterval karena berhenti saat tab tidak aktif.
CSS animation (@keyframes) dipicu dengan classList.add("kelas-animasi"). JavaScript menambah kelas, CSS menjalankan keyframes. Ini pola yang paling bersih: JS mengatur KAPAN, CSS mengatur BAGAIMANA.
CSS transition mendefinisikan BAGAIMANA animasi terjadi (0.3s ease); JavaScript toggle kelas .geser mengatur KAPAN. Kotak meluncur 150px sambil berputar dan berganti warna — semua dihaluskan browser.
Uji: simulasi animasi — toggle kelas "geser" mengubah posisi dari 0 ke 150.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
<!doctype html>
<html>
<head>
<style>
.kotak {
width: 60px; height: 60px;
background: teal;
transition: transform 0.3s ease, background 0.3s;
}
.geser { transform: translateX(150px) rotate(45deg); background: orange; }
</style>
</head>
<body>
<div class="kotak"></div>
<button id="btn">Animasikan</button>
<script>
const kotak = document.querySelector(".kotak");
document.getElementById("btn").addEventListener("click", () => {
kotak.classList.toggle("geser");
});
</script>
</body>
</html>CRUD lengkap dengan localStorage dan event delegation.
Proyek ini menggabungkan hampir semua yang dipelajari di bagian browser: selektor DOM, createElement, event listener, form handling, localStorage, dan event delegation. Hasilnya aplikasi Todo List yang bisa menambah, menandai selesai, menghapus, dan menyimpan data yang bertahan setelah reload.
Struktur: (1) form untuk input tugas baru; (2) ul untuk daftar; (3) localStorage untuk persist; (4) event delegation di ul untuk tombol per item. Data disimpan sebagai array objek: [{ teks, selesai }].
Kerangka sudah disiapkan — lengkapi bagian bertanda TODO sampai aplikasi berjalan penuh.
Aplikasi penuh: form menambah, checkbox menandai selesai, tombol menghapus, localStorage menyimpan. Event delegation di ul menangani semua interaksi item dengan satu listener. Latihan editor meminta fungsi penambah tugas yang mengubah array dan mengembalikan jumlah tugas.
Uji: fungsi tambahTugas(daftar, teks) menambah {teks, selesai:false} lalu kembalikan panjang daftar.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
<!doctype html>
<html>
<head>
<style>
body { font-family: sans-serif; max-width: 400px; margin: 40px auto; }
.selesai { text-decoration: line-through; opacity: 0.5; }
li { display: flex; align-items: center; gap: 8px; padding: 4px 0; }
</style>
</head>
<body>
<h1>Todo List</h1>
<form id="form">
<input id="input" placeholder="Tugas baru..." required>
<button>Tambah</button>
</form>
<ul id="daftar"></ul>
<script>
const form = document.getElementById("form");
const input = document.getElementById("input");
const daftar = document.getElementById("daftar");
let tugas = JSON.parse(localStorage.getItem("tugas") || "[]");
function simpan() {
localStorage.setItem("tugas", JSON.stringify(tugas));
}
function render() {
daftar.innerHTML = "";
tugas.forEach((t) => {
const li = document.createElement("li");
const centang = document.createElement("input");
centang.type = "checkbox";
centang.checked = t.selesai;
const teks = document.createElement("span");
teks.classList.toggle("selesai", t.selesai);
teks.textContent = t.teks;
const hapus = document.createElement("button");
hapus.type = "button";
hapus.textContent = "Hapus";
hapus.style.marginLeft = "auto";
li.append(centang, teks, hapus);
daftar.appendChild(li);
});
}
form.addEventListener("submit", (e) => {
e.preventDefault();
tugas.push({ teks: input.value, selesai: false });
input.value = "";
simpan(); render();
});
daftar.addEventListener("click", (e) => {
const idx = [...daftar.children].indexOf(e.target.closest("li"));
if (idx < 0) return;
if (e.target.type === "checkbox") tugas[idx].selesai = !tugas[idx].selesai;
else if (e.target.textContent === "Hapus") tugas.splice(idx, 1);
simpan(); render();
});
render();
</script>
</body>
</html>UI + event + state management sederhana.
Proyek penutup kedua: kalkulator dengan tombol angka, operator, dan layar hasil. Menggabungkan event delegation, state management (angka pertama, operator menunggu, angka kedua), dan update DOM real-time.
State-nya sederhana: firstNum (angka pertama), operator (+, -, *, /), waitingForSecond (boolean — sedang menunggu angka kedua), display (string di layar). Setiap tombol angka menambah digit; tombol operator menyimpan angka pertama dan operator; tombol = menghitung.
Kerangka sudah siap — lengkapi fungsi hitung dan update display sampai kalkulator berfungsi penuh.
Event delegation di #tombol menangani SEMUA tombol dengan satu listener. State: display untuk layar, firstNum untuk angka pertama, op untuk operator. Tombol = menghitung berdasarkan op yang tersimpan.
Uji: fungsi hitung(a, op, b) — hitung(10, "+", 20) harus `30`; hitung(10, "/", 0) harus `Error`.
Sandbox Judge0 memakai Node.js 12.14. Beberapa contoh materi memakai fitur JavaScript lebih baru; coba contoh tersebut di browser atau Node.js versi baru.
<!doctype html>
<html>
<head>
<style>
body { font-family: monospace; max-width: 280px; margin: 40px auto; }
#layar { font-size: 2em; text-align: right; padding: 12px; background: #222; color: #0f0; border-radius: 8px; margin-bottom: 8px; min-height: 1.4em; }
button { width: 60px; height: 50px; font-size: 1.2em; margin: 2px; border: 1px solid #ccc; border-radius: 6px; }
button:hover { background: #e0e0e0; }
.op { background: #ff9500; color: white; }
</style>
</head>
<body>
<div id="layar">0</div>
<div id="tombol">
<button>7</button><button>8</button><button>9</button><button class="op">/</button>
<button>4</button><button>5</button><button>6</button><button class="op">*</button>
<button>1</button><button>2</button><button>3</button><button class="op">-</button>
<button>0</button><button>.</button><button>C</button><button class="op">+</button>
<button style="width:100%">=</button>
</div>
<script>
const layar = document.getElementById("layar");
let display = "0", firstNum = null, op = null;
function update() { layar.textContent = display; }
document.getElementById("tombol").addEventListener("click", (e) => {
const t = e.target.textContent;
if (/[0-9.]/.test(t)) {
display = display === "0" ? t : display + t;
} else if (["+", "-", "*", "/"].includes(t)) {
firstNum = parseFloat(display);
op = t;
display = "0";
} else if (t === "C") {
display = "0"; firstNum = null; op = null;
} else if (t === "=") {
const b = parseFloat(display);
if (op === "+") display = firstNum + b;
else if (op === "-") display = firstNum - b;
else if (op === "*") display = firstNum * b;
else if (op === "/") display = b === 0 ? "Error" : firstNum / b;
firstNum = null; op = null;
}
update();
});
</script>
</body>
</html>