Apa itu Function?
Bayangin kamu lagi bikin kopi setiap pagi. Prosesnya selalu sama kan? Ambil gelas, tuang air panas, masukkin kopi, aduk, jadi deh. Nah, daripada nulis ulang semua langkah itu setiap kali mau bikin kopi, mending bikin satu "resep" yang bisa dipake berkali-kali. Itulah konsep function dalam programming.
Function itu blok kode yang bisa kamu pake berulang kali tanpa perlu nulis ulang. Kenapa penting? Karena programmer yang baik itu malas dalam arti positif - kita gak mau nulis kode yang sama berkali-kali.
Prinsip DRY: Don't Repeat Yourself
Ada prinsip dalam programming yang namanya DRY - Don't Repeat Yourself. Prinsip ini ngajarin kita buat menghindari duplikasi kode. Setiap kali kamu copy-paste kode yang sama, itu tandanya kamu perlu bikin function.
Misalnya kamu punya kode buat ngitung total harga di BuildWithAngga. Daripada nulis logika perhitungan itu di 10 tempat berbeda, mending bikin satu function aja. Nanti kalo ada perubahan, kamu cukup ubah di satu tempat doang. Simpel dan gak ribet.
Input, Process, Output
Konsep dasar function itu sederhana: input - process - output. Kayak mesin gitu. Kamu masukin sesuatu, mesin ngolah, terus keluar hasilnya. Dalam JavaScript, ada tiga komponen utama:
Parameter adalah input yang diterima function. Ini kayak placeholder buat data yang mau kamu proses. Misalnya function ngitung luas persegi panjang butuh panjang dan lebar sebagai parameter.
Body function adalah bagian dimana kamu nulis logika atau proses yang mau dilakukan. Di sinilah semua perhitungan dan operasi terjadi.
Return statement adalah output atau hasil dari function. Setelah proses selesai, function ngeluarin hasilnya lewat return. Kalo gak ada return, hasilnya undefined.
Contoh simpel: kamu bikin function ngitung total harga kursus di BuildWithAngga. Input-nya harga dan jumlah kursus, prosesnya perkalian, output-nya total yang harus dibayar. Sesederhana itu.
Yang perlu diinget, gak semua function harus punya semua komponen ini. Ada function tanpa input, ada juga yang gak perlu return value. Tapi konsep dasarnya tetep sama: function itu cara kita bikin kode yang reusable dan terorganisir dengan baik.
Function Declaration: Cara Klasik Bikin Function

Sekarang kita masuk ke cara pertama bikin function di JavaScript, namanya function declaration. Syntax-nya straightforward banget - mulai dengan keyword function, nama function-nya, parameter dalam kurung, dan body function dalam kurung kurawal:
function greet(name) {
return "Hello " + name;
}
Panggil dengan greet("Budi"), hasilnya "Hello Budi". Simple.
Hoisting: Keajaiban Function Declaration
Ini yang bikin function declaration spesial - namanya hoisting. Function declaration itu "diangkat" ke atas scope-nya. Artinya, kamu bisa manggil function sebelum deklarasinya:
console.log(sayHello("Angga")); // Output: "Hello Angga"
function sayHello(name) {
return "Hello " + name;
}
Kode di atas jalan tanpa error. JavaScript engine "mengangkat" semua function declaration ke paling atas sebelum eksekusi. Jadi dari sudut pandang JavaScript, function declaration udah ada dari awal.
Tapi inget, hoisting ini cuma berlaku buat function declaration doang. Function expression dan arrow function gak punya fitur ini.
Kapan Pake Function Declaration?
Function declaration paling cocok buat main logic atau core functionality di aplikasi. Misalnya di BuildWithAngga:
function calculateTotalPrice(price, quantity, discount) {
const subtotal = price * quantity;
const discountAmount = subtotal * (discount / 100);
return subtotal - discountAmount;
}
function validateEmail(email) {
const emailPattern = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;
return emailPattern.test(email);
}
function getUserCourses(userId) {
// logic fetch courses dari database
return courses;
}
Kenapa function declaration? Karena dia named function - punya nama yang jelas. Debugging jadi gampang. Stack trace langsung nunjukin nama function yang error, bukan cuma "anonymous function".
Naming Convention yang Baik
Pake camelCase buat nama function - kata pertama huruf kecil, kata berikutnya huruf besar. Contoh: getUserData, calculateTotal, validateForm. Nama harus jelasin apa yang function kerjain, jangan singkatan aneh yang cuma kamu yang ngerti.
Jangan declare function di dalam conditional atau loop. Ini bisa bikin behavior gak konsisten. Declare function di top level atau dalam scope function lain aja.
Function Expression: Assign Function ke Variable

Kalo di function declaration kita langsung kasih nama, di function expression kita assign function ke dalam variable. Konsepnya mirip kayak kamu simpen angka atau string ke variable, bedanya yang disimpen adalah function.
Syntax-nya kayak gini:
const greet = function(name) {
return "Hello " + name;
};
Liat bedanya? Function-nya gak punya nama (anonymous), terus disimpen ke variable greet. Sekarang kalo mau manggil, tinggal pake greet("Budi") sama kayak function declaration.
Gak Ada Hoisting di Sini
Ini perbedaan paling krusial dari function declaration - function expression TIDAK di-hoist. Artinya, kamu harus declare dulu sebelum dipake:
// Ini ERROR
console.log(sayHi("Angga")); // ReferenceError
const sayHi = function(name) {
return "Hi " + name;
};
Kenapa error? Karena JavaScript belom tau apa itu sayHi sebelum baris deklarasinya dieksekusi. Kalo pake var emang variable-nya di-hoist, tapi value-nya masih undefined, jadi tetep error pas dipanggil.
Yang bener kayak gini:
// Ini BENAR
const sayHi = function(name) {
return "Hi " + name;
};
console.log(sayHi("Angga")); // Output: "Hi Angga"
Declare dulu, baru pake. Simple rule yang harus diinget.
Kapan Pake Function Expression?
Function expression cocok dipake kalo kamu butuh assign function ke variable atau pass function sebagai argument. Use case paling umum adalah callback function.
Contoh di BuildWithAngga, misal kamu butuh filter kursus berdasarkan kategori:
const courses = [
{ title: "React Native", category: "mobile" },
{ title: "Next.js", category: "web" },
{ title: "Flutter", category: "mobile" }
];
const filterByCategory = function(category) {
return courses.filter(function(course) {
return course.category === category;
});
};
const mobileCourses = filterByCategory("mobile");
Function expression juga berguna kalo kamu mau reassign function atau bikin conditional function:
let calculate;
if (userIsPremium) {
calculate = function(price) {
return price * 0.8; // diskon 20%
};
} else {
calculate = function(price) {
return price;
};
}
Dengan function declaration, kamu gak bisa gini karena function name harus konsisten.
Anonymous vs Named Function Expression
Function expression bisa anonymous (tanpa nama) atau named (punya nama). Kebanyakan orang pake anonymous karena lebih simpel:
// Anonymous function expression
const greet = function(name) {
return "Hello " + name;
};
Tapi kadang kasih nama juga berguna, terutama buat debugging atau recursion:
// Named function expression
const factorial = function fact(n) {
if (n <= 1) return 1;
return n * fact(n - 1); // recursion pake nama internal
};
Nama fact di dalem function cuma visible di dalam function itu sendiri. Dari luar tetep dipanggil pake factorial. Ini ngebantu pas debugging karena stack trace bakal nunjukin nama fact bukan cuma "anonymous function".
Function Expression vs Function Declaration
Jadi kapan pake yang mana? Gampangnya gini:
Pake function declaration kalo:
- Main logic yang dipake di banyak tempat
- Butuh hoisting buat organisasi kode
- Function yang jelas punya tanggung jawab spesifik
Pake function expression kalo:
- Assign ke variable atau object property
- Pass sebagai callback atau argument
- Conditional function assignment
- Butuh kontrol lebih strict soal eksekusi order
Di project BuildWithAngga, biasanya API handlers atau utility functions pake declaration, sedangkan callback, event handlers, atau helper functions pake expression.
Intinya, function expression kasih fleksibilitas lebih dalam gimana kamu organize dan assign function. Tapi inget, gak ada hoisting jadi harus declare sebelom dipake.
Arrow Function: Syntax Modern JavaScript

Arrow function adalah cara modern nulis function di JavaScript, diperkenalkan di ES6. Syntax-nya lebih ringkas dan sering banget dipake di project modern. Bentuk dasarnya kayak gini:
const greet = (name) => {
return "Hello " + name;
};
Simbol => ini yang bikin namanya "arrow" function. Kalo dibaca keras, kayak "name arrow return Hello". Lebih pendek dari function expression kan?
Implicit Return: Makin Ringkas
Kalo function kamu cuma satu baris dan langsung return, kamu bisa ilangin kurung kurawal sama keyword return. Ini namanya implicit return:
// Dengan explicit return
const greet = (name) => {
return "Hello " + name;
};
// Dengan implicit return
const greet = (name) => "Hello " + name;
Keduanya ngasilin hasil yang sama, tapi yang bawah lebih clean. Di BuildWithAngga, kalo bikin helper function buat format harga misalnya:
const formatPrice = (price) => `Rp ${price.toLocaleString()}`;
const getDiscount = (price, percent) => price * (percent / 100);
const isFree = (price) => price === 0;
Simpel, jelas, dan gak buang-buang baris.
Variasi Syntax Arrow Function
Arrow function punya beberapa shorthand tergantung jumlah parameter:
// Single parameter - kurung opsional
const greet = name => "Hello " + name;
const square = x => x * x;
// Multiple parameters - kurung wajib
const add = (a, b) => a + b;
const calculate = (price, qty, disc) => price * qty - disc;
// No parameter - kurung kosong wajib
const sayHello = () => "Hello";
const getRandom = () => Math.random();
Tapi kalo body function lebih dari satu baris, tetep butuh kurung kurawal dan explicit return:
const processOrder = (items) => {
const total = items.reduce((sum, item) => sum + item.price, 0);
const tax = total * 0.1;
return total + tax;
};
Kapan Pake Arrow Function?
Arrow function paling cocok buat callback dan short functions. Contoh paling sering adalah array methods:
const courses = [
{ title: "React Native", price: 299000 },
{ title: "Next.js", price: 349000 },
{ title: "Flutter", price: 299000 }
];
// Filter kursus dibawah 300rb
const affordable = courses.filter(course => course.price < 300000);
// Mapping cuma ambil title
const titles = courses.map(course => course.title);
// Hitung total harga
const total = courses.reduce((sum, course) => sum + course.price, 0);
Bayangin kalo pake function expression biasa, bakal banyak banget keyword function. Arrow function bikin kode lebih readable.
Event handlers juga cocok pake arrow function:
button.addEventListener('click', () => {
console.log('Button clicked');
});
form.addEventListener('submit', (e) => {
e.preventDefault();
handleSubmit();
});
Warning: This Binding Berbeda
Ini yang bikin arrow function tricky - cara dia handle keyword this beda dari function biasa. Arrow function gak punya this sendiri, dia "inherit" dari parent scope. Ini topik advanced yang bakal bikin bingung sekarang, jadi skip dulu.
Yang perlu kamu tau sekarang: jangan pake arrow function buat method di object atau constructor function. Pake function declaration atau expression aja buat itu.
// JANGAN kayak gini
const user = {
name: "Budi",
greet: () => {
console.log("Hello " + this.name); // this.name undefined!
}
};
// Pake function expression
const user = {
name: "Budi",
greet: function() {
console.log("Hello " + this.name); // this work
}
};
Nanti kalo udah belajar tentang this keyword dan object-oriented JavaScript, baru balik lagi ke topik ini.
Arrow Function Best Practice
Pake arrow function buat:
- Array methods (map, filter, reduce, dll)
- Callback functions
- Short utility functions
- Event handlers
Jangan pake arrow function buat:
- Object methods
- Constructor functions
- Functions yang butuh
thisbinding - Functions yang butuh
argumentsobject
Kalo ragu, pake function expression aja. Tapi buat callback dan array methods, arrow function adalah standar di modern JavaScript. Project di BuildWithAngga hampir semua pake arrow function buat hal-hal kayak gini.
Syntax-nya emang butuh waktu buat terbiasa, tapi sekali ngerti, kamu bakal appreciate betapa clean dan concise-nya arrow function dibanding cara lama.
Parameter vs Argument: Bedanya Apa?

Banyak orang suka bingung bedain parameter sama argument. Padahal simpel kok. Parameter itu variable yang kamu tulis waktu define function, sedangkan argument itu nilai actual yang kamu kirim waktu manggil function.
function greet(name) { // 'name' ini parameter
return "Hello " + name;
}
greet("Budi"); // 'Budi' ini argument
Analoginya kayak gini: parameter itu kotak kosong yang nunggu diisi, argument itu isi yang kamu masukin ke kotak. Parameter itu placeholder, argument itu data asli.
Di BuildWithAngga misalnya:
function calculatePrice(price, quantity) { // price & quantity = parameter
return price * quantity;
}
const total = calculatePrice(299000, 3); // 299000 & 3 = argument
Gampang kan? Parameter ada di definisi function, argument ada pas kamu panggil function.
Default Parameter: Nilai Cadangan
Kadang kamu pengen kasih nilai default kalo argument gak dikasih. Di JavaScript modern, ini gampang banget pake default parameter:
function greet(name = "Guest") {
return "Hello " + name;
}
console.log(greet("Angga")); // "Hello Angga"
console.log(greet()); // "Hello Guest"
Kalo argument-nya undefined atau gak dikasih sama sekali, JavaScript bakal pake nilai default-nya. Ini berguna banget buat bikin function yang lebih fleksibel.
Contoh real di BuildWithAngga:
function enrollCourse(courseId, paymentMethod = "credit_card") {
console.log(`Enrolling course ${courseId} with ${paymentMethod}`);
}
enrollCourse("react-101"); // pake credit_card sebagai default
enrollCourse("vue-101", "bank_transfer"); // pake bank_transfer
Kamu juga bisa combine beberapa default parameter sekaligus:
function createUser(name, role = "student", isActive = true) {
return {
name: name,
role: role,
isActive: isActive
};
}
const user1 = createUser("Budi");
// { name: "Budi", role: "student", isActive: true }
const user2 = createUser("Siti", "instructor");
// { name: "Siti", role: "instructor", isActive: true }
const user3 = createUser("Andi", "admin", false);
// { name: "Andi", role: "admin", isActive: false }
Yang perlu diinget, default parameter cuma dipake kalo nilai-nya undefined. Kalo kamu pass null atau 0 atau false, nilai default gak dipake karena itu dianggep nilai yang valid.
Rest Parameter: Terima Banyak Argument
Gimana kalo kamu gak tau berapa banyak argument yang bakal dikirim? Di sinilah rest parameter berguna. Pake tiga titik ... sebelum nama parameter, dan semua argument bakal dikumpulin jadi array:
function sum(...numbers) {
let total = 0;
for (let num of numbers) {
total += num;
}
return total;
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(10, 20, 30, 40)); // 100
console.log(sum(5)); // 5
Rest parameter nge-collect semua argument sisanya jadi satu array. Makanya namanya "rest" - sisa-sisa argument dikumpulin.
Contoh praktis di BuildWithAngga, misal kamu mau tracking event dengan data yang bervariasi:
function trackEvent(eventName, ...eventData) {
console.log(`Event: ${eventName}`);
console.log("Data:", eventData);
}
trackEvent("course_enrolled", "react-101", "premium", 299000);
// Event: course_enrolled
// Data: ["react-101", "premium", 299000]
trackEvent("button_clicked", "checkout");
// Event: button_clicked
// Data: ["checkout"]
Rest parameter harus selalu di posisi terakhir. Kamu gak bisa punya parameter lain setelah rest parameter:
// BENAR
function process(action, ...items) { }
// SALAH - error!
function process(...items, action) { }
Kamu juga bisa combine rest parameter dengan default parameter:
function createNotification(title, message = "No message", ...tags) {
return {
title: title,
message: message,
tags: tags
};
}
const notif = createNotification("New Course", "React Native available", "mobile", "premium");
// { title: "New Course", message: "React Native available", tags: ["mobile", "premium"] }
Kapan Pake Apa?
Pake default parameter kalo:
- Ada nilai yang sering dipake dan masuk akal sebagai default
- Pengen bikin function lebih fleksibel tanpa perlu selalu pass semua argument
- Argument-nya opsional
Pake rest parameter kalo:
- Jumlah argument gak pasti atau bisa banyak
- Pengen kasih fleksibilitas user pass argument sebanyak yang mereka mau
- Bikin utility function kayak sum, merge, atau concat
Di real project, kamu bakal sering combine keduanya buat bikin function yang powerful tapi tetep simpel dipake. Yang penting, jangan lupa bedain parameter (di definition) sama argument (pas dipanggil).
Return Statement: Keluarin Hasil Function

Return statement itu cara function ngasih hasil balik ke kode yang manggilnya. Analoginya kayak kamu pesan makanan, restorannya masak, terus kasih makanan jadi ke kamu. Function process sesuatu, terus return hasilnya.
function multiply(a, b) {
return a * b;
}
const result = multiply(5, 3); // result = 15
Tanpa return, function gak ngasih apa-apa balik. Variable result bakal kosong atau undefined. Return itu output dari function yang bisa kamu pake atau simpen di variable.
Function Tanpa Return
Kalo function gak punya return statement, JavaScript otomatis return undefined. Banyak pemula gak nyadar ini dan bingung kenapa hasilnya undefined:
function greet(name) {
console.log("Hello " + name);
// gak ada return
}
const message = greet("Budi");
console.log(message); // undefined
Function di atas cuma print ke console, tapi gak return apa-apa. Kalo kamu mau hasil-nya bisa dipake, harus di-return:
function greet(name) {
return "Hello " + name;
}
const message = greet("Budi");
console.log(message); // "Hello Budi"
Bedain antara console.log sama return. Console.log cuma nampiliin ke browser console buat debugging, return ngasih nilai yang bisa dipake di kode lain.
Early Return: Stop Eksekusi Lebih Awal
Return juga bisa dipake buat stop eksekusi function di tengah jalan. Ini namanya early return, biasanya dipake buat validasi atau guard clause:
function enrollCourse(user, courseId) {
if (!user) {
return "User not found";
}
if (!courseId) {
return "Course ID required";
}
if (user.isPremium === false) {
return "Premium membership required";
}
// kalo semua validasi pass, baru proses enrollment
const enrollment = processEnrollment(user, courseId);
return enrollment;
}
Begitu ketemu return, function langsung berhenti dan keluar. Kode setelah return gak bakal dieksekusi. Ini bikin kode lebih clean daripada pake nested if-else yang dalem banget.
Early return juga bagus buat avoid "pyramid of doom":
// TANPA early return - susah dibaca
function checkout(cart, user, payment) {
if (cart.items.length > 0) {
if (user.isVerified) {
if (payment.isValid) {
// process checkout
return "Success";
} else {
return "Invalid payment";
}
} else {
return "User not verified";
}
} else {
return "Cart is empty";
}
}
// DENGAN early return - lebih clean
function checkout(cart, user, payment) {
if (cart.items.length === 0) return "Cart is empty";
if (!user.isVerified) return "User not verified";
if (!payment.isValid) return "Invalid payment";
// process checkout
return "Success";
}
Versi yang pake early return jauh lebih gampang dibaca kan?
Return Multiple Values
JavaScript gak bisa return lebih dari satu value secara langsung, tapi kamu bisa return object atau array yang isinya banyak value. Ini trik yang sering dipake:
// Return object
function getUserStats(userId) {
return {
totalCourses: 12,
completedCourses: 8,
inProgress: 4,
certificates: 5
};
}
const stats = getUserStats("user123");
console.log(stats.totalCourses); // 12
console.log(stats.certificates); // 5
Atau pake array kalo urutan-nya yang penting:
function getMinMax(numbers) {
const min = Math.min(...numbers);
const max = Math.max(...numbers);
return [min, max];
}
const [minimum, maximum] = getMinMax([5, 2, 9, 1, 7]);
console.log(minimum); // 1
console.log(maximum); // 9
Di BuildWithAngga, misalnya function yang process payment bisa return object dengan berbagai info:
function processPayment(amount, method) {
// proses payment logic
return {
success: true,
transactionId: "TRX123456",
amount: amount,
method: method,
timestamp: new Date()
};
}
const payment = processPayment(299000, "credit_card");
if (payment.success) {
console.log("Transaction ID:", payment.transactionId);
}
Return object itu lebih flexible karena kamu bisa akses value by name, gak perlu inget urutan. Plus, kalo nanti perlu tambahin data baru, tinggal tambahin property baru tanpa break kode yang udah ada.
Best Practice Return Statement
Selalu return sesuatu yang konsisten. Jangan kadang return string, kadang return object, kadang return boolean dari function yang sama. Ini bikin susah diprediksi.
Kalo function buat validasi, return boolean (true/false). Kalo function buat ambil data, return data-nya atau null kalo gak ketemu. Kalo function buat process sesuatu, return status atau result object.
Pake early return buat validasi dan error handling. Ini bikin main logic function kamu tetep clean di bagian bawah tanpa nested if yang ribet.
Return statement itu simple tapi powerful. Dia yang bikin function bener-bener useful karena hasil process-nya bisa dipake dimana-mana di aplikasi kamu.
Apa itu Scope?

Scope itu menentukan dimana variable bisa diakses di kode kamu. Bayangin scope kayak kamar-kamar di rumah. Barang yang ada di ruang tamu bisa dilihat semua orang, tapi barang di kamar kamu cuma kamu yang bisa akses. Scope bekerja dengan cara yang sama.
Ada tiga jenis scope utama di JavaScript: global scope, local scope, dan block scope. Ngerti perbedaan ketiganya penting banget buat avoid bug yang susah dilacak.
Global Scope: Accessible Everywhere
Variable yang dideclare di luar semua function punya global scope. Artinya, variable ini bisa diakses dari mana aja di kode kamu:
const siteName = "BuildWithAngga";
function showSite() {
console.log(siteName); // bisa akses
}
function getSite() {
return siteName; // bisa akses juga
}
console.log(siteName); // bisa akses dimana aja
Global variable itu praktis, tapi jangan kebanyakan. Kenapa? Karena semua kode bisa ubah nilai-nya, jadi susah tracking kalo ada bug. Plus, kalo pake library atau framework lain, bisa aja nama variable-nya bentrok.
Pake global scope cuma buat config atau constant yang emang perlu diakses dimana-mana:
const API_URL = "<https://api.buildwithangga.com>";
const MAX_UPLOAD_SIZE = 5242880; // 5MB
Local Scope: Hanya Dalam Function
Variable yang dideclare di dalam function cuma bisa diakses dari dalam function itu. Di luar function, variable ini gak exist:
function calculateDiscount() {
const discount = 0.2; // local scope
const price = 100000; // local scope
return price * discount;
}
console.log(discount); // Error: discount is not defined
console.log(price); // Error: price is not defined
Ini bagus buat encapsulation - data di dalam function gak bisa diutak-atik dari luar. Setiap function punya "ruang pribadi" buat variable-nya sendiri.
Function yang berbeda bisa punya variable dengan nama yang sama tanpa konflik:
function greetMorning() {
const message = "Good morning";
return message;
}
function greetNight() {
const message = "Good night";
return message;
}
// gak ada konflik karena masing-masing punya scope sendiri
Block Scope: Let dan Const Only
Block scope itu scope yang dibuat oleh kurung kurawal {}. Variable yang dideclare pake let atau const di dalam block cuma bisa diakses di block itu:
if (true) {
let blockVar = "I'm in a block";
const anotherBlock = "Me too";
console.log(blockVar); // bisa akses
}
console.log(blockVar); // Error: blockVar is not defined
Ini berlaku buat semua jenis block - if statement, loop, atau bahkan block biasa:
{
const temp = "temporary";
console.log(temp); // bisa
}
console.log(temp); // Error
for (let i = 0; i < 3; i++) {
console.log(i); // bisa
}
console.log(i); // Error: i is not defined
Block scope ini ngebantu banget buat avoid variable yang gak sengaja ke-overwrite atau dipake di tempat yang gak seharusnya.
Var: Avoid di Modern JavaScript
Variable yang dideclare pake var gak punya block scope, cuma function scope. Ini bikin behavior yang aneh dan sering bikin bug:
if (true) {
var username = "Budi";
}
console.log(username); // "Budi" - masih bisa diakses!
for (var i = 0; i < 3; i++) {
// loop
}
console.log(i); // 3 - masih exist!
Makanya di modern JavaScript, jangan pake var lagi. Selalu pake let atau const. Const buat variable yang gak bakal diubah, let buat yang bakal diubah:
const API_KEY = "abc123"; // gak bakal berubah
let counter = 0; // bakal diubah
Lexical Scope: Access Parent Scope
Function di JavaScript bisa akses variable dari parent scope-nya. Ini namanya lexical scoping atau static scoping:
const courseName = "React Native";
function showCourse() {
const instructor = "Angga";
function displayInfo() {
// bisa akses courseName (global) dan instructor (parent function)
console.log(`${courseName} by ${instructor}`);
}
displayInfo();
}
showCourse(); // "React Native by Angga"
Inner function bisa akses variable dari outer function, tapi gak sebaliknya. Ini kayak hirarki - anak bisa akses barang ortu, tapi ortu gak bisa akses barang anak.
Contoh praktis di BuildWithAngga:
function createCourse(title) {
const createdAt = new Date();
function publish() {
// bisa akses title dan createdAt dari parent
console.log(`Publishing ${title} created at ${createdAt}`);
}
function draft() {
console.log(`Drafting ${title}`);
}
return { publish, draft };
}
const course = createCourse("Next.js Master");
course.publish(); // bisa akses title dari parent scope
Lexical scope ini jadi fondasi dari closure yang bakal kita bahas di section berikutnya.
Scope Chain
JavaScript cari variable dengan urutan tertentu. Mulai dari scope paling dalam, terus ke parent, terus ke global. Ini namanya scope chain:
const global = "global";
function outer() {
const outerVar = "outer";
function inner() {
const innerVar = "inner";
console.log(innerVar); // cari di inner scope - ketemu
console.log(outerVar); // gak ada di inner, cari di outer - ketemu
console.log(global); // gak ada di inner & outer, cari di global - ketemu
}
inner();
}
outer();
Kalo variable gak ketemu sampe global scope, baru JavaScript throw error "is not defined".
Ngerti scope penting banget buat avoid bug dan bikin kode yang predictable. Selalu pake const atau let, jangan var. Dan inget, inner scope bisa akses outer scope, tapi gak sebaliknya.
Closure: Function yang Inget Parent Scope

Closure itu konsep yang kedengeran ribet tapi sebenernya simple. Closure terjadi waktu function bisa "inget" dan akses variable dari outer scope-nya, bahkan setelah outer function udah selesai dieksekusi.
Bayangin kayak gini: kamu punya kotak yang isinya function sama variable. Bahkan kalo kotak udah ditutup, function di dalemnya masih bisa akses variable yang ada di kotak itu. Itulah closure.
function createGreeting(greeting) {
// variable 'greeting' ada di outer scope
return function(name) {
// inner function bisa akses 'greeting' dari outer scope
return `${greeting}, ${name}!`;
};
}
const sayHello = createGreeting("Hello");
const sayHi = createGreeting("Hi");
console.log(sayHello("Budi")); // "Hello, Budi!"
console.log(sayHi("Siti")); // "Hi, Siti!"
Function sayHello dan sayHi itu closure - mereka "inget" nilai greeting dari waktu mereka dibuat, walaupun createGreeting udah selesai dijalankan.
Practical Use: Data Privacy
Salah satu kegunaan closure adalah bikin data yang private. Di JavaScript gak ada keyword private seperti bahasa lain, tapi kamu bisa pake closure buat nyimpen data yang gak bisa diakses dari luar:
function createBankAccount(initialBalance) {
let balance = initialBalance; // private variable
return {
deposit: function(amount) {
balance += amount;
return balance;
},
withdraw: function(amount) {
if (amount > balance) {
return "Insufficient funds";
}
balance -= amount;
return balance;
},
getBalance: function() {
return balance;
}
};
}
const myAccount = createBankAccount(100000);
console.log(myAccount.getBalance()); // 100000
myAccount.deposit(50000); // 150000
myAccount.withdraw(30000); // 120000
// balance gak bisa diakses langsung dari luar
console.log(myAccount.balance); // undefined
Variable balance cuma bisa diakses lewat method yang kita sediain. Orang luar gak bisa langsung ubah-ubah balance sesuka hati. Ini konsep encapsulation yang powerful banget.
Factory Pattern dengan Closure
Closure juga sering dipake buat factory pattern - function yang nge-generate function lain dengan konfigurasi tertentu. Contoh di BuildWithAngga:
function createCourseFilter(category) {
return function(course) {
return course.category === category;
};
}
const courses = [
{ title: "React Native", category: "mobile" },
{ title: "Next.js", category: "web" },
{ title: "Flutter", category: "mobile" },
{ title: "Vue.js", category: "web" }
];
const filterMobile = createCourseFilter("mobile");
const filterWeb = createCourseFilter("web");
const mobileCourses = courses.filter(filterMobile);
const webCourses = courses.filter(filterWeb);
console.log(mobileCourses); // React Native, Flutter
console.log(webCourses); // Next.js, Vue.js
Setiap filter function "inget" kategori yang dikasih pas dia dibuat. Ini bikin kode lebih reusable dan flexible.
Counter Function: Contoh Klasik
Ini contoh paling sering dipake buat jelasin closure - counter function:
function createCounter() {
let count = 0;
return {
increment: function() {
count++;
return count;
},
decrement: function() {
count--;
return count;
},
getCount: function() {
return count;
}
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.increment()); // 3
console.log(counter.decrement()); // 2
console.log(counter.getCount()); // 2
Variable count tetep exist dan "diinget" sama function-function yang di-return. Setiap kali kamu panggil increment atau decrement, dia update count yang sama. Ini magic-nya closure.
Kamu bisa bikin multiple counter yang independent:
const counter1 = createCounter();
const counter2 = createCounter();
counter1.increment(); // 1
counter1.increment(); // 2
counter2.increment(); // 1
console.log(counter1.getCount()); // 2
console.log(counter2.getCount()); // 1
Setiap counter punya count sendiri yang terpisah. Ini karena setiap kali kamu call createCounter, dia bikin scope baru dengan variable count baru.
Kapan Pake Closure?
Closure berguna kalo kamu butuh:
- Private variable yang gak bisa diakses langsung dari luar
- Function yang "inget" konfigurasi atau state tertentu
- Factory function yang generate function dengan behavior berbeda
- Event handler yang perlu akses ke variable tertentu
Contoh praktis di BuildWithAngga buat tracking course completion:
function createProgressTracker(courseId) {
let completedLessons = [];
return {
markComplete: function(lessonId) {
if (!completedLessons.includes(lessonId)) {
completedLessons.push(lessonId);
}
},
getProgress: function(totalLessons) {
return (completedLessons.length / totalLessons) * 100;
},
isComplete: function(lessonId) {
return completedLessons.includes(lessonId);
}
};
}
const reactProgress = createProgressTracker("react-101");
reactProgress.markComplete("lesson-1");
reactProgress.markComplete("lesson-2");
console.log(reactProgress.getProgress(10)); // 20%
Skip Deep Dive - Itu Aja Dulu
Closure itu topik advanced yang punya banyak nuansa dan edge cases. Buat sekarang, yang penting kamu ngerti konsep dasar: function bisa "inget" dan akses variable dari outer scope-nya, bahkan setelah outer function selesai.
Kalo mau explore lebih dalam tentang closure, memory management, atau performance implications-nya, itu bisa di lain waktu. Yang penting sekarang kamu tau closure exist dan bisa dipake buat data privacy sama factory pattern.
Nanti kalo udah lebih advanced dan mulai bikin aplikasi kompleks, closure bakal jadi tool yang sering kamu pake tanpa sadar. Banyak pattern di JavaScript modern yang rely on closure, termasuk di React hooks atau Node.js modules.
Single Responsibility Principle

Satu function sebaiknya cuma ngerjain satu hal. Jangan bikin function yang ngerjain A, B, C sekaligus. Kalo function kamu susah dikasih nama yang jelas, kemungkinan besar dia ngerjain terlalu banyak hal.
// BURUK - function ngerjain terlalu banyak
function processUser(user) {
// validasi user
if (!user.email) return false;
// simpan ke database
database.save(user);
// kirim email welcome
sendEmail(user.email, "Welcome!");
// update analytics
analytics.track("user_registered");
return true;
}
// BAGUS - pecah jadi beberapa function
function validateUser(user) {
return user.email !== undefined;
}
function saveUser(user) {
return database.save(user);
}
function sendWelcomeEmail(email) {
return sendEmail(email, "Welcome!");
}
function trackRegistration() {
return analytics.track("user_registered");
}
function registerUser(user) {
if (!validateUser(user)) return false;
saveUser(user);
sendWelcomeEmail(user.email);
trackRegistration();
return true;
}
Dengan cara ini, setiap function punya tanggung jawab yang jelas. Lebih gampang di-test, di-debug, dan di-maintain.
Meaningful Name: Jelasin Apa yang Dilakukan
Nama function harus jelasin apa yang dikerjakan, bukan cuma label random. Pake verb di awal nama function karena function itu action:
// BURUK - nama gak jelas
function data() { }
function process() { }
function doIt() { }
function handle() { }
// BAGUS - nama deskriptif
function getUserData() { }
function calculateTotalPrice() { }
function validateEmail() { }
function formatCurrency() { }
Nama yang spesifik lebih baik daripada nama yang generic. getUserById lebih jelas daripada getUser. calculateDiscountedPrice lebih jelas daripada calculate.
Di BuildWithAngga, contoh nama yang baik:
function enrollUserToCourse(userId, courseId) { }
function checkPremiumMembership(user) { }
function generateCourseCertificate(courseId, userId) { }
function sendPaymentConfirmation(transactionId) { }
Dari nama aja udah langsung tau function ngapain tanpa harus buka isinya.
Short Parameters: Maksimal 3
Kalo function punya lebih dari 3 parameter, jadi susah diinget urutannya. Pake object buat pass banyak parameter:
// BURUK - terlalu banyak parameter
function createCourse(title, description, price, duration, instructor, category, level) {
// implementasi
}
// susah inget urutan parameter
createCourse("React", "Belajar React", 299000, 120, "Angga", "web", "beginner");
// BAGUS - pake object
function createCourse(courseData) {
const { title, description, price, duration, instructor, category, level } = courseData;
// implementasi
}
// lebih jelas dan gak peduli urutan
createCourse({
title: "React",
description: "Belajar React",
price: 299000,
instructor: "Angga",
category: "web",
level: "beginner",
duration: 120
});
Dengan object, kamu bisa skip parameter yang opsional, gak perlu pass undefined atau null. Plus, kalo nanti mau tambahin parameter baru, tinggal tambahin property di object tanpa break existing code.
Pure Function: Predictable Output
Pure function itu function yang kalo dikasih input yang sama, selalu ngasih output yang sama. Gak ada side effect - gak ngubah variable di luar, gak manggil API, gak update DOM.
// BURUK - impure function (punya side effect)
let total = 0;
function addToTotal(amount) {
total += amount; // ngubah variable luar
return total;
}
// BAGUS - pure function
function add(a, b) {
return a + b; // cuma return hasil, gak ubah apapun
}
// BURUK - impure (result beda-beda)
function getRandomDiscount() {
return Math.random() * 100;
}
// BAGUS - pure (result konsisten)
function calculateDiscount(price, percentage) {
return price * (percentage / 100);
}
Pure function lebih gampang di-test karena predictable. Kamu gak perlu setup state atau mock external dependencies. Input A selalu ngasih output B, simple.
Di BuildWithAngga, contoh pure function:
// Pure - gak ada side effect
function formatPrice(price) {
return `Rp ${price.toLocaleString()}`;
}
function calculateProgress(completed, total) {
return (completed / total) * 100;
}
function isValidEmail(email) {
return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email);
}
Kalo function kamu harus punya side effect (misal save ke database), pisahin logic bisnis dengan side effect:
// Logic bisnis - pure
function calculateOrderTotal(items, discountCode) {
const subtotal = items.reduce((sum, item) => sum + item.price, 0);
const discount = getDiscount(discountCode);
return subtotal - discount;
}
// Side effect - impure
function saveOrder(orderData) {
database.orders.insert(orderData); // side effect
}
// Combine keduanya
function processOrder(items, discountCode) {
const total = calculateOrderTotal(items, discountCode);
saveOrder({ items, total });
return total;
}
Early Return: Avoid Deep Nesting
Udah dibahas di section return statement, tapi worth diulang karena ini important banget. Pake early return buat validasi, jangan bikin pyramid of doom:
// BURUK - deep nesting
function enrollCourse(user, course) {
if (user) {
if (user.isPremium) {
if (course.isAvailable) {
if (user.balance >= course.price) {
// main logic di sini, kejauhan dari kiri
return processEnrollment(user, course);
} else {
return "Insufficient balance";
}
} else {
return "Course not available";
}
} else {
return "Premium membership required";
}
} else {
return "User not found";
}
}
// BAGUS - early return
function enrollCourse(user, course) {
if (!user) return "User not found";
if (!user.isPremium) return "Premium membership required";
if (!course.isAvailable) return "Course not available";
if (user.balance < course.price) return "Insufficient balance";
return processEnrollment(user, course);
}
Versi dengan early return jauh lebih clean dan gampang dibaca.
Comments untuk Why, Bukan What
Jangan comment hal yang udah jelas dari kode. Comment buat jelasin kenapa kamu bikin keputusan tertentu, bukan apa yang kode lakukan:
// BURUK - comment apa yang kode lakukan (obvious)
// Loop through courses
for (let course of courses) {
// Add course to array
result.push(course);
}
// BAGUS - comment kenapa
// Filter premium courses only because free users
// should only see paid content in search results
const premiumCourses = courses.filter(c => c.isPremium);
// BURUK
// Set discount to 20%
const discount = 0.2;
// BAGUS
// 20% discount for early bird promo (campaign #2024-Q1)
const EARLY_BIRD_DISCOUNT = 0.2;
Kode yang baik itu self-documenting - dari nama variable dan function aja udah jelas ngapain. Comment cuma buat context atau reasoning yang gak bisa dijelasin di kode.
Combine Semua Best Practice
Contoh function yang apply semua best practice di atas:
/**
* Calculate course price with applicable discounts
* Note: Early bird discount only valid until end of month
*/
function calculateCoursePrice({ basePrice, userType, promoCode }) {
// Validate inputs
if (!basePrice || basePrice <= 0) return 0;
if (!userType) return basePrice;
// Apply user type discount
const typeDiscount = getUserTypeDiscount(userType);
const priceAfterType = basePrice * (1 - typeDiscount);
// Apply promo code if valid
const promoDiscount = getPromoDiscount(promoCode);
const finalPrice = priceAfterType * (1 - promoDiscount);
return Math.round(finalPrice);
}
// Helper functions (pure, single responsibility)
function getUserTypeDiscount(userType) {
const discounts = {
premium: 0.2,
student: 0.15,
regular: 0
};
return discounts[userType] || 0;
}
function getPromoDiscount(code) {
// Check if promo code valid and not expired
if (!code || !isValidPromo(code)) return 0;
return 0.1; // 10% promo discount
}
Function di atas punya nama jelas, parameter pake object, logic predictable, ada early return, dan comment jelasin why bukan what.
Best practice ini gak cuma bikin kode lebih clean, tapi juga lebih maintainable dan less prone to bugs. Worth the effort!
Higher-Order Functions: Function yang Nerima atau Return Function

Higher-order function kedengeran fancy, tapi konsepnya simple. Ini adalah function yang bisa nerima function lain sebagai parameter, atau return function sebagai hasil. Intinya, function yang "main" dengan function lain.
Di JavaScript, function itu first-class citizen - artinya function bisa diperlakukan kayak data biasa. Bisa disimpen di variable, di-pass sebagai argument, atau di-return dari function lain. Higher-order function memanfaatin konsep ini.
// Function yang nerima function lain sebagai parameter
function executeOperation(a, b, operation) {
return operation(a, b);
}
const add = (x, y) => x + y;
const multiply = (x, y) => x * y;
console.log(executeOperation(5, 3, add)); // 8
console.log(executeOperation(5, 3, multiply)); // 15
Function executeOperation adalah higher-order function karena dia nerima function lain (operation) sebagai parameter.
Contoh Built-in: setTimeout dan addEventListener
Kamu sebenernya udah sering pake higher-order function tanpa sadar. setTimeout dan addEventListener adalah contoh paling umum:
// setTimeout nerima function sebagai parameter
setTimeout(function() {
console.log("Muncul setelah 2 detik");
}, 2000);
// atau pake arrow function
setTimeout(() => {
console.log("Ini juga muncul setelah 2 detik");
}, 2000);
// addEventListener juga nerima function
const button = document.querySelector("#myButton");
button.addEventListener("click", function() {
console.log("Button clicked!");
});
Function yang kamu pass ke setTimeout atau addEventListener itu namanya callback function. Mereka dipanggil nanti pas event tertentu terjadi.
Callback Function: Function as Argument
Callback adalah function yang di-pass sebagai argument ke function lain. Function yang nerima callback bakal "call back" function itu di waktu yang tepat:
function processCourseData(courseId, callback) {
// simulasi fetch data dari database
const course = {
id: courseId,
title: "React Native",
price: 299000
};
// panggil callback dengan data yang udah di-fetch
callback(course);
}
// callback function
function displayCourse(course) {
console.log(`${course.title} - Rp ${course.price}`);
}
processCourseData("react-101", displayCourse);
// Output: "React Native - Rp 299000"
Callback bikin kode kamu lebih flexible. Kamu bisa pass callback yang berbeda tergantung apa yang mau dilakuin dengan data:
// callback buat display
processCourseData("react-101", (course) => {
console.log(course.title);
});
// callback buat save
processCourseData("react-101", (course) => {
localStorage.setItem("lastViewed", course.id);
});
// callback buat track analytics
processCourseData("react-101", (course) => {
analytics.track("course_viewed", { courseId: course.id });
});
Array Methods: Higher-Order Functions Paling Sering Dipake
Array methods kayak map, filter, dan reduce adalah higher-order functions. Mereka nerima callback function buat process setiap element:
const courses = [
{ title: "React Native", price: 299000 },
{ title: "Next.js", price: 349000 },
{ title: "Flutter", price: 299000 }
];
// map - transform setiap element
const titles = courses.map(course => course.title);
// ["React Native", "Next.js", "Flutter"]
// filter - filter element berdasarkan kondisi
const affordable = courses.filter(course => course.price < 300000);
// [{ title: "React Native", ... }, { title: "Flutter", ... }]
// reduce - reduce jadi satu nilai
const totalPrice = courses.reduce((sum, course) => sum + course.price, 0);
// 947000
Callback yang kamu pass ke array methods di-execute buat setiap element di array. Ini bikin kode jauh lebih clean daripada pake loop biasa.
Function yang Return Function
Higher-order function juga bisa return function lain. Ini sering dipake buat create specialized functions:
function createMultiplier(multiplier) {
return function(number) {
return number * multiplier;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
Di BuildWithAngga, contoh praktis buat create validator functions:
function createValidator(minLength) {
return function(value) {
return value.length >= minLength;
};
}
const validateUsername = createValidator(3);
const validatePassword = createValidator(8);
console.log(validateUsername("ab")); // false
console.log(validateUsername("angga")); // true
console.log(validatePassword("pass")); // false
console.log(validatePassword("password123")); // true
Function yang di-return ini "inget" nilai minLength dari parent scope - ini konsep closure yang udah kita bahas sebelumnya.
Use Case: Async Operations
Higher-order functions sangat berguna buat async operations. Callback dipake buat handle result pas async operation selesai:
function fetchCourseData(courseId, onSuccess, onError) {
// simulasi API call
setTimeout(() => {
const success = Math.random() > 0.2;
if (success) {
const data = { id: courseId, title: "React Native" };
onSuccess(data);
} else {
onError("Failed to fetch course");
}
}, 1000);
}
// pake callbacks
fetchCourseData(
"react-101",
(data) => console.log("Success:", data),
(error) => console.log("Error:", error)
);
Pattern ini dasar dari async programming di JavaScript. Walaupun sekarang ada Promise dan async/await yang lebih modern, konsep callback masih penting buat dipaham.
Kenapa Higher-Order Functions Penting?
Higher-order functions bikin kode lebih reusable dan composable. Daripada nulis logic yang sama berulang kali, kamu bisa extract logic-nya jadi function dan pass behavior yang berbeda lewat callback.
Ini juga bikin kode lebih declarative - kamu bilang apa yang mau dilakukan, bukan gimana cara ngelakuinnya step by step. Bandingkan:
// Imperative (how)
const titles = [];
for (let i = 0; i < courses.length; i++) {
titles.push(courses[i].title);
}
// Declarative (what)
const titles = courses.map(course => course.title);
Versi dengan map lebih jelas intent-nya - kita mau transform courses jadi titles. Gak perlu mikir soal loop counter atau index.
Next Topic: Deep Dive
Konsep higher-order function ini cuma intro. Nanti bakal ada topic khusus yang deep dive ke array methods (map, filter, reduce, dll), async programming (Promises, async/await), dan function composition.
Yang penting sekarang kamu ngerti konsep dasarnya: function bisa nerima function lain sebagai parameter, atau return function sebagai hasil. Konsep sederhana ini yang bikin JavaScript sangat powerful dan flexible.
Lupa Return Statement

Ini kesalahan paling umum yang sering bikin bingung pemula. Function yang seharusnya ngasih nilai balik, tapi malah return undefined karena lupa kasih return:
// SALAH - lupa return
function calculateTotal(price, quantity) {
const total = price * quantity;
// lupa return!
}
const result = calculateTotal(100, 5);
console.log(result); // undefined - harusnya 500
// BENAR
function calculateTotal(price, quantity) {
const total = price * quantity;
return total; // atau langsung: return price * quantity;
}
Ini sering kejadian waktu kamu biasa pake console.log buat debugging, terus lupa ganti jadi return. Inget, console.log cuma nampiliin ke console, bukan return value.
// SALAH - console.log bukan return
function greet(name) {
console.log("Hello " + name); // ini cuma print, bukan return
}
const message = greet("Budi");
console.log(message); // undefined
// BENAR
function greet(name) {
return "Hello " + name; // ini baru return
}
Arrow function dengan implicit return juga bisa jadi jebakan. Kalo kamu pake kurung kurawal, harus ada return. Kalo gak pake kurung, otomatis return:
// SALAH - pake kurung tapi lupa return
const double = (x) => {
x * 2; // ini gak di-return!
};
// BENAR - pake kurung dengan return
const double = (x) => {
return x * 2;
};
// atau BENAR - tanpa kurung (implicit return)
const double = (x) => x * 2;
Confusing Parameter dengan Argument
Banyak pemula yang bingung bedain parameter sama argument, padahal ini konsep dasar yang penting. Parameter itu di definisi function, argument itu pas manggil:
// 'price' dan 'qty' adalah PARAMETER
function calculatePrice(price, qty) {
return price * qty;
}
// 100 dan 5 adalah ARGUMENT
const total = calculatePrice(100, 5);
Kesalahan umum adalah salah urutan argument:
function enrollCourse(userId, courseId, paymentMethod) {
// implementasi
}
// SALAH - urutan argument kebalik
enrollCourse("credit_card", "react-101", "user123");
// BENAR - urutan sesuai parameter
enrollCourse("user123", "react-101", "credit_card");
Makanya lebih baik pake object buat function dengan banyak parameter, jadi gak perlu inget urutan:
function enrollCourse({ userId, courseId, paymentMethod }) {
// implementasi
}
// gak perlu inget urutan
enrollCourse({
courseId: "react-101",
userId: "user123",
paymentMethod: "credit_card"
});
Arrow Function This Binding
Arrow function punya behavior berbeda soal this keyword. Ini jebakan buat pemula yang baru belajar:
const user = {
name: "Budi",
courses: ["React", "Vue"],
// SALAH - arrow function di object method
showCourses: () => {
console.log(this.name); // this.name = undefined!
console.log(this.courses); // this.courses = undefined!
}
};
user.showCourses(); // undefined, error!
Arrow function gak punya this sendiri, jadi this refer ke parent scope (window/global). Buat object method, jangan pake arrow function:
const user = {
name: "Budi",
courses: ["React", "Vue"],
// BENAR - function expression
showCourses: function() {
console.log(this.name); // "Budi"
console.log(this.courses); // ["React", "Vue"]
}
// atau BENAR - method shorthand
// showCourses() {
// console.log(this.name);
// }
};
Tapi arrow function bagus dipake di dalam method buat preserve this:
const user = {
name: "Budi",
courses: ["React", "Vue"],
showCourses: function() {
// arrow function di sini OK, this tetep refer ke user
this.courses.forEach((course) => {
console.log(`${this.name} enrolled in ${course}`);
});
}
};
Parameter Lebih dari 3
Function dengan terlalu banyak parameter susah dipake dan di-maintain. Kalo udah lebih dari 3 parameter, waktunya refactor:
// BURUK - terlalu banyak parameter
function createUser(name, email, age, city, country, phone, role, status) {
// susah banget inget urutan dan apa aja yang harus di-pass
}
// call-nya ribet dan error-prone
createUser("Budi", "[email protected]", 25, "Jakarta", "Indonesia", "08123", "student", "active");
Solusinya, pake object:
// BAGUS - pake object
function createUser(userData) {
const { name, email, age, city, country, phone, role, status } = userData;
// implementasi
}
// call-nya jelas dan gak perlu inget urutan
createUser({
name: "Budi",
email: "[email protected]",
age: 25,
city: "Jakarta",
country: "Indonesia",
phone: "08123",
role: "student",
status: "active"
});
Atau kalo beberapa parameter related, grup jadi object:
// BAGUS - group related parameters
function createUser(personalInfo, contactInfo, accountInfo) {
// personalInfo = { name, age }
// contactInfo = { email, phone }
// accountInfo = { role, status }
}
Side Effect di Function
Function dengan side effect susah diprediksi dan di-test. Side effect itu waktu function ngubah sesuatu di luar scope-nya:
// BURUK - side effect everywhere
let totalRevenue = 0;
function processSale(amount) {
totalRevenue += amount; // ngubah global variable
localStorage.setItem("revenue", totalRevenue); // side effect
sendAnalytics("sale", amount); // side effect
return amount;
}
Function kayak gini unpredictable. Kalo dipanggil beberapa kali, hasil-nya bisa beda-beda tergantung state global. Testing-nya juga susah karena harus setup state dulu.
Lebih baik pisahin pure logic dengan side effect:
// BAGUS - pure function buat logic
function calculateNewRevenue(currentRevenue, saleAmount) {
return currentRevenue + saleAmount;
}
// BAGUS - function terpisah buat side effect
function saveTotalRevenue(amount) {
localStorage.setItem("revenue", amount);
}
function trackSale(amount) {
sendAnalytics("sale", amount);
}
// combine waktu dipake
function processSale(amount) {
const newRevenue = calculateNewRevenue(totalRevenue, amount);
totalRevenue = newRevenue;
saveTotalRevenue(newRevenue);
trackSale(amount);
return amount;
}
Dengan cara ini, function calculateNewRevenue pure dan gampang di-test. Side effect dikumpulin di satu tempat dan jelas.
Tips: Make Function Pure dan Predictable
Sebisa mungkin bikin function yang pure - same input always gives same output, no side effects:
// PURE - predictable
function calculateDiscount(price, percentage) {
return price * (percentage / 100);
}
// berapapun dipanggil, input sama = output sama
calculateDiscount(100, 10); // always 10
calculateDiscount(100, 10); // always 10
// IMPURE - unpredictable
function getRandomDiscount(price) {
const random = Math.random();
return price * random;
}
// setiap dipanggil, output beda-beda
getRandomDiscount(100); // bisa 23.5
getRandomDiscount(100); // bisa 67.2
Pure function lebih gampang:
- Di-test: gak perlu mock atau setup
- Di-debug: input-output jelas
- Di-reuse: bisa dipake dimana aja tanpa worry soal state
- Di-optimize: JavaScript engine bisa cache hasil
Kalo emang harus ada side effect, minimal isolate dan dokumentasiin dengan jelas. Jangan sembunyiin side effect di tengah-tengah logic:
// BAGUS - side effect jelas dari nama
function saveAndNotifyUser(user) {
database.save(user); // side effect
sendEmail(user.email); // side effect
return user;
}
// lebih baik lagi - pisahin
function saveUser(user) {
return database.save(user);
}
function notifyUser(user) {
return sendEmail(user.email);
}
Intinya, bikin function yang behavior-nya predictable. Orang yang baca kode kamu harus bisa tau apa yang function lakukan cuma dari nama dan parameter-nya, tanpa harus baca semua implementasinya.
Saatnya Praktek: Challenge Function

Setelah belajar semua konsep function dari declaration sampai best practice, sekarang waktunya apply ilmu yang udah kamu pelajari. Ini tiga challenge yang bakal ngetes pemahaman kamu. Coba kerjain sendiri dulu sebelum liat solusinya.
Challenge 1: Calculate Total Harga
Buat function yang ngitung total harga berdasarkan quantity dan price. Function ini harus:
- Nerima dua parameter:
pricedanquantity - Return hasil perkalian keduanya
- Handle edge case kalo parameter gak valid (misal negatif atau bukan angka)
Coba kerjain sendiri dulu. Jangan langsung scroll ke bawah.
Udah coba? Ini salah satu solusinya:
function calculateTotalPrice(price, quantity) {
// validasi input
if (typeof price !== 'number' || typeof quantity !== 'number') {
return 'Invalid input: price and quantity must be numbers';
}
if (price < 0 || quantity < 0) {
return 'Invalid input: price and quantity must be positive';
}
// hitung total
return price * quantity;
}
// test function
console.log(calculateTotalPrice(299000, 3)); // 897000
console.log(calculateTotalPrice(150000, 2)); // 300000
console.log(calculateTotalPrice(-100, 5)); // "Invalid input..."
console.log(calculateTotalPrice("100", 5)); // "Invalid input..."
Atau versi yang lebih advanced dengan discount:
function calculateTotalPrice(price, quantity, discount = 0) {
// validasi
if (price < 0 || quantity < 0 || discount < 0 || discount > 100) {
return 'Invalid input';
}
const subtotal = price * quantity;
const discountAmount = subtotal * (discount / 100);
return subtotal - discountAmount;
}
console.log(calculateTotalPrice(100000, 2)); // 200000
console.log(calculateTotalPrice(100000, 2, 10)); // 180000 (diskon 10%)
Challenge 2: Validate Email Format
Buat function buat validasi format email. Function ini harus:
- Nerima satu parameter:
email(string) - Return
truekalo format email valid - Return
falsekalo format email gak valid - Email valid minimal punya format:
[email protected]
Coba dulu sebelum liat solusi.
Ini solusinya:
function validateEmail(email) {
// cek apakah email adalah string
if (typeof email !== 'string') {
return false;
}
// regex pattern buat validasi email
const emailPattern = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;
return emailPattern.test(email);
}
// test function
console.log(validateEmail("[email protected]")); // true
console.log(validateEmail("[email protected]")); // true
console.log(validateEmail("invalid.email")); // false
console.log(validateEmail("@nodomain.com")); // false
console.log(validateEmail("no@domain")); // false
console.log(validateEmail(123)); // false
Penjelasan regex-nya:
^= start of string[^\\s@]+= satu atau lebih karakter yang bukan spasi atau @@= literal @ symbol[^\\s@]+= satu atau lebih karakter setelah @\\.= literal dot (.)[^\\s@]+= satu atau lebih karakter setelah dot$= end of string
Atau versi yang lebih strict dengan validasi tambahan:
function validateEmail(email) {
if (typeof email !== 'string') return false;
// cek panjang minimal
if (email.length < 5) return false;
// cek ada @ dan .
if (!email.includes('@') || !email.includes('.')) return false;
// regex validation
const emailPattern = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$/;
return emailPattern.test(email);
}
console.log(validateEmail("[email protected]")); // true
console.log(validateEmail("[email protected]")); // false (terlalu pendek)
console.log(validateEmail("test@email")); // false (gak ada domain extension)
Challenge 3: Greeting dengan Default Parameter
Buat function greeting yang:
- Nerima dua parameter:
namedantimeOfDay timeOfDaypunya default value "day"- Return greeting message sesuai waktu (morning, afternoon, evening, night)
- Kalo
namegak dikasih, pake "Guest" sebagai default
Coba kerjain sendiri dulu.
Ini solusinya:
function greet(name = "Guest", timeOfDay = "day") {
const greetings = {
morning: "Good morning",
afternoon: "Good afternoon",
evening: "Good evening",
night: "Good night",
day: "Hello"
};
const greeting = greetings[timeOfDay] || greetings.day;
return `${greeting}, ${name}!`;
}
// test function
console.log(greet("Budi", "morning")); // "Good morning, Budi!"
console.log(greet("Siti", "evening")); // "Good evening, Siti!"
console.log(greet("Angga")); // "Hello, Angga!"
console.log(greet()); // "Hello, Guest!"
console.log(greet("Ahmad", "night")); // "Good night, Ahmad!"
Atau versi dengan auto-detect time:
function greet(name = "Guest", autoDetect = false) {
let timeOfDay = "day";
if (autoDetect) {
const hour = new Date().getHours();
if (hour < 12) timeOfDay = "morning";
else if (hour < 17) timeOfDay = "afternoon";
else if (hour < 21) timeOfDay = "evening";
else timeOfDay = "night";
}
const greetings = {
morning: "Good morning",
afternoon: "Good afternoon",
evening: "Good evening",
night: "Good night",
day: "Hello"
};
return `${greetings[timeOfDay]}, ${name}!`;
}
console.log(greet("Budi", true)); // auto-detect berdasarkan jam sekarang
console.log(greet()); // "Hello, Guest!"
Bonus Challenge: Combine Semuanya
Coba combine ketiga function di atas jadi satu flow yang realistis:
function processCourseEnrollment(userData) {
const { name, email, coursePrice, quantity } = userData;
// validasi email
if (!validateEmail(email)) {
return { success: false, message: "Invalid email format" };
}
// calculate total
const total = calculateTotalPrice(coursePrice, quantity);
if (typeof total === 'string') {
return { success: false, message: total };
}
// greeting message
const welcomeMessage = greet(name, "day");
return {
success: true,
message: `${welcomeMessage} Your enrollment is confirmed!`,
total: total,
email: email
};
}
// test
const result = processCourseEnrollment({
name: "Angga",
email: "[email protected]",
coursePrice: 299000,
quantity: 2
});
console.log(result);
// {
// success: true,
// message: "Hello, Angga! Your enrollment is confirmed!",
// total: 598000,
// email: "[email protected]"
// }
Next Topic: Arrays & Array Methods
Selamat! Kamu udah selesai belajar fundamental tentang functions di JavaScript. Dari function declaration, expression, arrow function, sampai best practices dan common mistakes.
Topic berikutnya adalah Arrays & Array Methods. Kamu bakal belajar tentang map, filter, reduce, dan method-method lain yang heavily rely on functions dan callbacks. Konsep higher-order function yang udah kamu pelajari bakal sangat berguna di topic berikutnya.
Sebelum lanjut, pastiin kamu:
- Ngerti perbedaan function declaration, expression, dan arrow function
- Bisa bikin function dengan parameter dan return value
- Paham konsep scope dan closure (minimal basic-nya)
- Tau best practice bikin function yang clean dan maintainable
- Udah coba semua challenge di atas
Kalo masih ada yang kurang jelas, boleh balik lagi ke section sebelumnya. Function itu fondasi penting di JavaScript, jadi pastiin kamu comfortable dengan konsep-konsep ini sebelum lanjut ke topic yang lebih advanced.
Keep coding and see you di episode berikutnya tentang Arrays & Array Methods!