Menguasai JavaScript modern (ES6+): let/const, arrow function, destructuring, optional chaining, manipulasi DOM & events, async dengan promises dan async/await, serta ES modules — lalu menghidupkan keranjang belanja TokoKita dengan vanilla JS

Setelah di episode 4 halaman TokoKita tampil bagus secara visual, kini saatnya menghidupkannya: JavaScript — satu-satunya bahasa yang berjalan di browser. Tanpa JavaScript, halaman hanya statis: tombol "Tambah ke keranjang" tidak melakukan apa-apa. Dengan JavaScript, halaman bereaksi, mengambil data, dan berinteraksi dengan pengguna.
Episode ini fokus pada JS modern (ES6+). Banyak tutorial lama mengajarkan var, function declaration, dan document.write — kita tidak akan ke sana. Sintaks modern bukan sekadar gaya: ia menuliskan maksud kode lebih jelas dan mencegah bug kelas pemula. Pemahaman di episode ini adalah fondasi mutlak untuk TypeScript (episode 6) dan React (episode 8).
let count = 0; // bisa diubah
const PRICE = 85000; // konstan — default pilihan
const add = (a, b) => a + b;Gunakan const secara default, let hanya saat nilai harus berubah. Arrow function lebih ringkas dan tidak punya this sendiri — perbedaan this ini akan terasa di React nanti.
const product = { name: "Kopi Arabika", price: 85000, stock: 12 };
const { name, price } = product;
const cart = [];
const addItem = (item) => [...cart, item];
const user = null;
console.log(user?.name); // undefined, bukan error
console.log(user?.address?.city); // aman walau nested...cart (spread) membuat array baru, bukan memutasi yang lama — pola yang dipakai terus di state management (episode 11). Optional chaining (?.) mencegah error saat properti belum ada.
DOM adalah representasi halaman yang bisa dimanipulasi JavaScript:
const button = document.querySelector(".btn-add");
const cartCount = document.querySelector("#cart-count");
button.addEventListener("click", () => {
cartCount.textContent = Number(cartCount.textContent) + 1;
});Dua pilar DOM yang paling sering dipakai: querySelector/querySelectorAll untuk memilih elemen, dan addEventListener untuk merespons event (klik, submit, input, scroll).
Jika ada banyak tombol (misal 50 kartu produk), jangan pasang listener satu per satu — pasang satu listener di elemen induk:
document.querySelector(".product-grid").addEventListener("click", (e) => {
const btn = e.target.closest(".btn-add");
if (!btn) return;
console.log("Tambah:", btn.dataset.productId);
});closest() mencari elemen terdekat yang cocok, dan dataset membaca atribut data-product-id. Pola ini membuat kode tetap cepat walau ribuan produk.
Banyak operasi frontend butuh waktu: mengambil data dari API, memuat gambar, mengirim pesanan. Promise merepresentasikan nilai yang belum tersedia. Cara paling jelas membaca Promise adalah async/await:
async function loadProducts() {
try {
const res = await fetch("/api/products");
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const products = await res.json();
renderProducts(products);
} catch (error) {
console.error("Gagal memuat produk:", error);
}
}Important
JavaScript adalah single-threaded: operasi lambat (fetch, timeout) tidak boleh memblokir thread utama, karena thread yang sama juga dipakai untuk merender. async/await memastikan UI tetap responsif selama menunggu — inilah inti mengapa React tanpa framework data masih "lengket" dan kita akan pakai TanStack Query di episode 11.
Pitfall khas pemula: lupa res.ok. fetch tidak melempar error untuk HTTP 404/500 — kalian harus memeriksanya sendiri, seperti contoh di atas.
import/export memecah aplikasi menjadi file kecil yang saling bergantung secara eksplisit:
export const cart = [];
export function addToCart(product) {
cart.push(product);
}import { addToCart } from "./cart.js";
addToCart({ id: 1, name: "Kopi Arabika", price: 85000 });Modules membuat dependency terlihat dan mencegah variabel global bentrok. Ini fondasi arsitektur aplikasi — bundler di episode 14 memakai mekanisme yang sama.
Gabungkan semuanya dalam keranjang vanilla JS:
const state = {
items: [],
total: 0,
};
function addItem(product) {
state.items = [...state.items, product];
render();
}
function render() {
const total = state.items.reduce((sum, item) => sum + item.price, 0);
document.querySelector("#cart-total").textContent = `Rp${total.toLocaleString("id-ID")}`;
document.querySelector("#cart-count").textContent = state.items.length;
}
document.querySelector(".product-grid").addEventListener("click", (e) => {
const btn = e.target.closest(".btn-add");
if (!btn) return;
addItem({ id: btn.dataset.productId, price: Number(btn.dataset.price) });
});| Kesalahan | Dampak | Perbaikan |
|---|---|---|
var dan global scope | Bug nilai yang tumpang tindih | const/let + modules |
Mutasi array langsung (push pada state) | Sulit dilacak & di-test | Spread untuk state baru |
fetch tanpa cek res.ok | Error 404/500 tidak tertangkap | Cek status lalu throw |
| Listener per elemen | Lambat & bocor memori | Event delegation |
| Callback bersarang | "Callback hell" | async/await |
Inti yang harus dibawa pulang:
const/let, arrow function, destructuring, spread, optional chaining — adalah sintaks modern yang wajib.querySelector + addEventListener, dengan event delegation untuk banyak elemen.async/await + fetch, selalu cek res.ok, jangan blokir main thread.import/export) memecah aplikasi menjadi bagian yang terkelola.Di episode 6 selanjutnya kita menaikkan standar: TypeScript — menambahkan tipe pada JavaScript. Di 2026 TypeScript praktis wajib di tim frontend, dan framework-framework besar (Next.js, Vite, TanStack) semuanya berasumsi kalian memakainya. Sampai jumpa di episode 6!