Bab 5: Fungsi Lanjutan

10 menit baca

Kalau di bab sebelumnya kamu belajar bikin fungsi dasar, sekarang kita naik level. Bab ini tentang teknik-teknik canggih yang bikin fungsi JavaScript jadi super fleksibel. Ini yang bikin JavaScript beda dari bahasa lain.


5.1 Rekursi dan Stack

Apa Itu Rekursi?

Analogi: Bayangkan kamu berdiri di antara dua cermin. Kamu lihat pantulan dirimu yang memantulkan dirimu lagi, dan lagi, dan lagi... sampai akhirnya berhenti (karena cermin ada batasnya). Itulah rekursi — fungsi yang memanggil dirinya sendiri.

javascript
function hitungMundur(angka) {
  console.log(angka);
  if (angka <= 0) return; // titik berhenti (base case)
  hitungMundur(angka - 1); // panggil diri sendiri
}

hitungMundur(5); // 5, 4, 3, 2, 1, 0

Dua Komponen Wajib Rekursi

  1. Base case — kondisi berhenti (tanpa ini = infinite loop = crash)
  2. Recursive step — panggil diri sendiri dengan masalah yang lebih kecil

Contoh Klasik: Pangkat

javascript
// Iteratif (pakai loop)
function pangkatLoop(x, n) {
  let hasil = 1;
  for (let i = 0; i < n; i++) {
    hasil *= x;
  }
  return hasil;
}

// Rekursif (panggil diri sendiri)
function pangkat(x, n) {
  if (n === 1) return x;          // base case
  return x * pangkat(x, n - 1);   // recursive step
}

console.log(pangkat(2, 4)); // 16
// Cara kerjanya:
// pangkat(2,4) = 2 * pangkat(2,3)
// pangkat(2,3) = 2 * pangkat(2,2)
// pangkat(2,2) = 2 * pangkat(2,1)
// pangkat(2,1) = 2 ← base case, mulai balik

Execution Context Stack

Setiap kali fungsi dipanggil, JavaScript menyimpan "konteks" di tumpukan (stack):

pangkat(2,1) ← sedang jalan [paling atas] pangkat(2,2) ← nunggu pangkat(2,3) ← nunggu pangkat(2,4) ← nunggu [paling bawah]

Begitu base case tercapai, stack "dibongkar" dari atas ke bawah. Makanya rekursi pakai memori — setiap panggilan nambah satu layer di stack.

Recursive Traversal (Jelajah Rekursif)

Rekursi paling berguna untuk data bersarang (nested):

javascript
const perusahaan = {
  marketing: [
    { nama: "Andi", gaji: 5000000 },
    { nama: "Budi", gaji: 4500000 }
  ],
  engineering: {
    frontend: [{ nama: "Cici", gaji: 7000000 }],
    backend: [{ nama: "Dedi", gaji: 7500000 }]
  }
};

function totalGaji(departemen) {
  if (Array.isArray(departemen)) {
    // Base case: array karyawan → jumlahkan
    return departemen.reduce((total, org) => total + org.gaji, 0);
  }
  // Recursive step: objek → jelajahi sub-departemen
  let total = 0;
  for (const subDept of Object.values(departemen)) {
    total += totalGaji(subDept);
  }
  return total;
}

console.log(totalGaji(perusahaan)); // 24000000

Linked List (Daftar Berantai)

Struktur data rekursif — setiap elemen punya value dan pointer ke elemen next:

javascript
const daftar = {
  value: 1,
  next: {
    value: 2,
    next: {
      value: 3,
      next: null // akhir daftar
    }
  }
};

// Cetak semua nilai (rekursif)
function cetakDaftar(daftar) {
  console.log(daftar.value);
  if (daftar.next) cetakDaftar(daftar.next);
}

cetakDaftar(daftar); // 1, 2, 3
⚠️Jebakan!
  • Lupa base case = stack overflow (program crash)
  • JavaScript punya batas stack (~10.000 panggilan). Jangan rekursi untuk data sangat besar
  • Rekursi lebih lambat dari loop (karena overhead stack), tapi kodenya lebih bersih
🎯Challenge

Buat fungsi fibonacci(n) yang mengembalikan angka Fibonacci ke-n. Fibonacci: 1, 1, 2, 3, 5, 8, 13...

javascript
// fibonacci(1) = 1
// fibonacci(7) = 13
// Hint: fib(n) = fib(n-1) + fib(n-2), base case: fib(1) = fib(2) = 1

5.2 Rest Parameter dan Spread Syntax

Rest Parameter (...)

Analogi: Kamu pesan makanan di restoran. "Saya mau nasi goreng, terus... sisanya terserah chef." Rest parameter = "sisanya".

javascript
function totalBelanja(diskon, ...hargaBarang) {
  // diskon = parameter pertama
  // hargaBarang = ARRAY berisi semua argumen sisanya
  const total = hargaBarang.reduce((sum, h) => sum + h, 0);
  return total - diskon;
}

console.log(totalBelanja(5000, 20000, 15000, 30000)); // 60000
// diskon = 5000
// hargaBarang = [20000, 15000, 30000]

Aturan: Rest parameter HARUS di posisi terakhir.

javascript
// ❌ SALAH
function salah(...angka, terakhir) {}

// ✅ BENAR
function benar(pertama, ...sisanya) {}

Spread Syntax (...)

Kebalikan dari rest — membongkar array/objek jadi elemen terpisah.

javascript
const angka = [3, 7, 1, 9, 4];

// Tanpa spread — Math.max butuh argumen terpisah, bukan array
console.log(Math.max(angka)); // NaN 😢

// Dengan spread — array dibongkar jadi 3, 7, 1, 9, 4
console.log(Math.max(...angka)); // 9 🎉

// Gabung array
const buah = ["apel", "jeruk"];
const sayur = ["bayam", "wortel"];
const semua = [...buah, ...sayur, "tempe"];
// ["apel", "jeruk", "bayam", "wortel", "tempe"]

Spread untuk Copy Objek/Array

javascript
// Copy array (shallow copy)
const asli = [1, 2, 3];
const salinan = [...asli];
salinan.push(4);
console.log(asli);    // [1, 2, 3] — tidak berubah!
console.log(salinan); // [1, 2, 3, 4]

// Copy objek
const user = { nama: "Andi", umur: 25 };
const userBaru = { ...user, kota: "Jakarta" };
// { nama: "Andi", umur: 25, kota: "Jakarta" }
⚠️Jebakan!
  • ... di parameter fungsi = rest (mengumpulkan)
  • ... di pemanggilan/literal = spread (membongkar)
  • Spread hanya shallow copy — objek nested tetap berbagi referensi
🎯Challenge

Buat fungsi gabungUnik(...arrays) yang menerima beberapa array dan mengembalikan satu array berisi semua elemen unik (tanpa duplikat).

javascript
// gabungUnik([1,2,3], [2,3,4], [4,5]) → [1,2,3,4,5]

5.3 Scope Variabel dan Closure

Scope (Cakupan)

Analogi: Bayangkan rumah dengan kamar-kamar. Barang di kamar A tidak bisa diakses dari kamar B. Tapi barang di ruang tamu (global) bisa diakses dari mana saja.

javascript
// Scope global
const namaApp = "TokoKu";

function tampilkanProduk() {
  // Scope lokal fungsi
  const produk = "Sepatu";
  console.log(namaApp); // ✅ Bisa akses global
  console.log(produk);  // ✅ Bisa akses lokal
}

console.log(namaApp); // ✅
console.log(produk);  // ❌ Error! produk tidak ada di sini

Block Scope

Variabel let dan const hanya hidup di dalam {} tempat mereka dideklarasikan:

javascript
if (true) {
  let rahasia = "abc123";
  console.log(rahasia); // ✅ "abc123"
}
console.log(rahasia); // ❌ Error! Tidak ada di luar block

for (let i = 0; i < 3; i++) {
  // i hanya ada di sini
}
console.log(i); // ❌ Error!

Lexical Environment

Setiap kali kode berjalan (fungsi dipanggil, block {} dimasuki), JavaScript membuat Lexical Environment — semacam "tas" yang menyimpan variabel lokal + referensi ke environment luar.

┌─────────────────────────┐ │ Lexical Environment │ │ - variabel lokal │ │ - referensi ke outer → ─┼──→ [Environment luar] └─────────────────────────┘

Closure — Konsep Paling Penting!

Analogi: Kamu punya kotak bekal dari rumah. Mau kamu bawa ke kantor, ke taman, ke mana pun — isi kotak tetap dari rumah. Closure = fungsi yang "membawa bekal" dari tempat dia dibuat.

javascript
function buatCounter() {
  let hitungan = 0; // variabel "bekal"

  return function() {
    hitungan++;
    return hitungan;
  };
}

const counter = buatCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3

// hitungan "hidup" di dalam closure, tidak bisa diakses dari luar
// console.log(hitungan); // ❌ Error!

Kenapa ini penting?

  • Closure = cara bikin data privat di JavaScript
  • Setiap panggilan buatCounter() bikin closure BARU (independen)
javascript
const counterA = buatCounter();
const counterB = buatCounter();

console.log(counterA()); // 1
console.log(counterA()); // 2
console.log(counterB()); // 1 ← independen!

Closure di Dunia Nyata

javascript
// Fungsi pembuat greeting
function buatGreeting(salam) {
  return function(nama) {
    return `${salam}, ${nama}!`;
  };
}

const halo = buatGreeting("Halo");
const selamat = buatGreeting("Selamat pagi");

console.log(halo("Andi"));     // "Halo, Andi!"
console.log(selamat("Budi"));  // "Selamat pagi, Budi!"
⚠️Jebakan!
  • Closure mengakses nilai terkini variabel, bukan salinan saat dibuat:
javascript
let nama = "Andi";

function sapa() {
  console.log(`Hai, ${nama}`);
}

nama = "Budi"; // diubah sebelum fungsi dipanggil
sapa(); // "Hai, Budi" — bukan "Andi"!
  • Loop + closure = jebakan klasik:
javascript
// ❌ BUG: semua alert menampilkan 10
for (var i = 0; i < 10; i++) {
  setTimeout(() => console.log(i), 100);
}
// Output: 10, 10, 10, ... (10 kali)

// ✅ FIX: pakai let (setiap iterasi punya scope sendiri)
for (let i = 0; i < 10; i++) {
  setTimeout(() => console.log(i), 100);
}
// Output: 0, 1, 2, 3, ..., 9
🎯Challenge

Buat fungsi buatAkumulator(nilaiAwal) yang mengembalikan fungsi. Setiap kali fungsi itu dipanggil dengan angka, dia menambahkan angka tersebut ke total dan mengembalikan total baru.

javascript
const akum = buatAkumulator(10);
console.log(akum(5));  // 15
console.log(akum(3));  // 18
console.log(akum(7));  // 25

5.4 "var" yang Lama

Kenapa Perlu Tahu?

Kamu akan sering ketemu var di kode lama. Penting paham bedanya supaya tidak kena bug misterius.

Perbedaan var vs let/const

Fiturvarlet/const
ScopeFunction scopeBlock scope
HoistingYa (diangkat ke atas)Ya tapi "dead zone"
Re-deklarasiBolehError

Function Scope (Bukan Block Scope!)

javascript
if (true) {
  var pesan = "Halo"; // var TIDAK peduli block!
}
console.log(pesan); // "Halo" ← masih bisa diakses!

// Bandingkan dengan let:
if (true) {
  let pesan2 = "Hai";
}
// console.log(pesan2); // ❌ Error!

Hoisting (Diangkat ke Atas)

javascript
console.log(x); // undefined (bukan Error!)
var x = 5;

// JavaScript "membaca" kode di atas seperti ini:
// var x;           ← deklarasi diangkat ke atas
// console.log(x); ← undefined karena belum diisi
// x = 5;          ← assignment tetap di tempat

IIFE (Immediately Invoked Function Expression)

Dulu, sebelum ada let, programmer pakai trik ini untuk bikin scope:

javascript
// Pola IIFE — langsung jalan setelah dideklarasikan
(function() {
  var rahasia = "abc123";
  console.log(rahasia); // ✅
})();

// console.log(rahasia); // ❌ Error — terkurung di dalam IIFE
⚠️Jebakan!
  • Jangan pakai var di kode baru — selalu let/const
  • var di dalam for loop bocor keluar:
javascript
for (var i = 0; i < 3; i++) {}
console.log(i); // 3 ← bocor!

for (let j = 0; j < 3; j++) {}
// console.log(j); // ❌ Error — aman
🎯Challenge

Tebak output kode ini (tanpa menjalankan):

javascript
var a = 1;
function test() {
  console.log(a); // ?
  var a = 2;
  console.log(a); // ?
}
test();
console.log(a); // ?

5.5 Objek Global

Apa Itu?

Objek global = "wadah" untuk semua yang bersifat global. Di browser namanya window, di Node.js namanya global (atau globalThis yang universal).

javascript
// Di browser:
var nama = "Andi"; // var bikin property di window!
console.log(window.nama); // "Andi"

// let/const TIDAK bikin property di window
let umur = 25;
console.log(window.umur); // undefined

globalThis — Universal

javascript
// Bekerja di mana saja (browser, Node.js, dll)
globalThis.appNama = "TokoKu";
console.log(globalThis.appNama); // "TokoKu"

Kapan Pakai Objek Global?

Hampir tidak pernah untuk menyimpan data. Tapi berguna untuk:

  • Cek fitur browser: if (window.Promise)
  • Polyfill: if (!window.fetch) { window.fetch = ... }
⚠️Jebakan!
  • Jangan polusi global scope — bikin konflik nama
  • Pakai module system (import/export) untuk berbagi data antar file
🎯Challenge

Buat polyfill sederhana: cek apakah globalThis.Array.prototype.at ada. Kalau tidak, tambahkan method at yang mengambil elemen array berdasarkan index (termasuk index negatif).


5.6 Function Object dan NFE

Fungsi = Objek

Di JavaScript, fungsi itu objek. Artinya fungsi punya property!

javascript
function sapa(nama) {
  console.log(`Hai, ${nama}`);
}

// Property bawaan
console.log(sapa.name);   // "sapa" — nama fungsi
console.log(sapa.length); // 1 — jumlah parameter

// Bisa tambah property sendiri
sapa.jumlahPanggilan = 0;

Property name

javascript
// Function declaration
function halo() {}
console.log(halo.name); // "halo"

// Function expression
const selamat = function() {};
console.log(selamat.name); // "selamat" — diambil dari variabel!

// Method di objek
const obj = {
  metode() {},
  metode2: function() {}
};
console.log(obj.metode.name);  // "metode"
console.log(obj.metode2.name); // "metode2"

Property length

Menghitung jumlah parameter (tidak termasuk rest ...):

javascript
function f1(a) {}
function f2(a, b) {}
function f3(a, b, ...rest) {}

console.log(f1.length); // 1
console.log(f2.length); // 2
console.log(f3.length); // 2 — rest tidak dihitung!

Custom Property (Pengganti Closure)

javascript
function buatCounter() {
  function counter() {
    return counter.hitungan++;
  }
  counter.hitungan = 0;
  return counter;
}

const hitung = buatCounter();
console.log(hitung()); // 0
console.log(hitung()); // 1
console.log(hitung.hitungan); // 2 — bisa diakses dari luar!

Bedanya dengan closure: property bisa diakses dari luar, closure tidak.

NFE (Named Function Expression)

javascript
// Function Expression biasa
const sapa = function(nama) {
  console.log(`Hai, ${nama}`);
};

// Named Function Expression — punya nama internal
const sapa2 = function sapaDalam(nama) {
  if (!nama) {
    sapaDalam("Anonim"); // bisa panggil diri sendiri!
    return;
  }
  console.log(`Hai, ${nama}`);
};

sapa2(); // "Hai, Anonim"
// sapaDalam(); // ❌ Error — nama hanya ada di dalam fungsi
⚠️Jebakan!
  • function.length tidak menghitung parameter dengan default value
  • Property fungsi bukan variabel lokal — tidak ada hubungannya dengan closure
🎯Challenge

Buat fungsi buatCounter() yang mengembalikan fungsi counter. Counter harus punya method reset() untuk mengembalikan hitungan ke 0.

javascript
const counter = buatCounter();
console.log(counter()); // 0
console.log(counter()); // 1
counter.reset();
console.log(counter()); // 0

5.7 Sintaks "new Function"

Membuat Fungsi dari String

javascript
// Sintaks: new Function('param1', 'param2', 'body')
const jumlah = new Function('a', 'b', 'return a + b');

console.log(jumlah(3, 5)); // 8

Kapan Dipakai?

Sangat jarang! Biasanya hanya ketika:

  • Menerima kode dari server
  • Membuat fungsi secara dinamis dari template

Perbedaan Penting: Tidak Punya Closure!

javascript
function biasa() {
  const x = 10;
  const fn = new Function('return x'); // ❌ tidak bisa akses x!
  return fn();
}
// biasa(); // Error: x is not defined

function biasa2() {
  const x = 10;
  const fn = () => x; // ✅ closure normal
  return fn();
}
console.log(biasa2()); // 10

Fungsi dari new Function hanya bisa akses variabel global, bukan variabel lokal di sekitarnya.

⚠️Jebakan!
  • Hindari new Function kecuali benar-benar perlu
  • Mirip eval() — potensi masalah keamanan
  • Tidak bisa di-optimize oleh engine karena body berupa string
🎯Challenge

Buat fungsi buatKalkulator(operasi) yang menerima string operasi ("+", "-", "*", "/") dan mengembalikan fungsi yang melakukan operasi tersebut. Gunakan new Function.

javascript
const kali = buatKalkulator("*");
console.log(kali(3, 4)); // 12

5.8 Scheduling: setTimeout dan setInterval

setTimeout — Jalankan Sekali Setelah Delay

Analogi: Kamu set alarm untuk 5 menit lagi. Alarm bunyi sekali, selesai.

javascript
// Sintaks: setTimeout(fungsi, delay_ms, ...argumen)
function sapa(nama) {
  console.log(`Halo, ${nama}!`);
}

// Jalankan sapa setelah 2 detik (2000 ms)
setTimeout(sapa, 2000, "Andi");
// Setelah 2 detik: "Halo, Andi!"

Membatalkan setTimeout

javascript
const timerId = setTimeout(() => console.log("Bom!"), 5000);

// Berubah pikiran — batalkan!
clearTimeout(timerId);
// "Bom!" tidak akan pernah muncul

setInterval — Jalankan Berulang

Analogi: Alarm yang bunyi setiap 5 menit, terus-terusan sampai kamu matikan.

javascript
// Cetak waktu setiap 1 detik
const intervalId = setInterval(() => {
  console.log(new Date().toLocaleTimeString());
}, 1000);

// Hentikan setelah 5 detik
setTimeout(() => {
  clearInterval(intervalId);
  console.log("Timer dihentikan");
}, 5000);

setTimeout Rekursif (Alternatif setInterval)

javascript
// Lebih fleksibel — bisa atur delay berbeda tiap iterasi
let delay = 1000;

function polling() {
  console.log("Cek server...");
  // Kalau gagal, tunggu lebih lama
  delay *= 2; // exponential backoff
  setTimeout(polling, delay);
}

setTimeout(polling, delay);

Keuntungan vs setInterval:

  • Delay dihitung SETELAH fungsi selesai (bukan dari mulai)
  • Bisa ubah delay secara dinamis

Zero Delay setTimeout

javascript
console.log("1 - sebelum");

setTimeout(() => console.log("2 - timeout"), 0);

console.log("3 - sesudah");

// Output:
// "1 - sebelum"
// "3 - sesudah"
// "2 - timeout" ← tetap terakhir! (masuk antrian)

Meskipun delay = 0, fungsi tetap masuk antrian dan dijalankan setelah kode sinkron selesai.

⚠️Jebakan!
  • setTimeout delay minimum, bukan tepat. Kalau CPU sibuk, bisa lebih lama
  • Di browser, nested setTimeout punya minimum delay 4ms setelah 5 panggilan
  • this hilang di setTimeout:
javascript
const user = {
  nama: "Andi",
  sapa() {
    console.log(`Hai, ${this.nama}`);
  }
};

setTimeout(user.sapa, 1000); // "Hai, undefined" 😱
// Fix:
setTimeout(() => user.sapa(), 1000); // "Hai, Andi" ✅
🎯Challenge

Buat fungsi cetakBerurutan(pesan, delay) yang mencetak setiap karakter dari pesan satu per satu dengan jeda delay ms.

javascript
cetakBerurutan("Halo!", 300);
// H (setelah 300ms)
// a (setelah 600ms)
// l (setelah 900ms)
// o (setelah 1200ms)
// ! (setelah 1500ms)

5.9 Decorator dan Forwarding (call/apply)

Apa Itu Decorator?

Analogi: Kamu punya kue polos. Decorator = menambah topping tanpa mengubah kue aslinya. Di programming, decorator = fungsi yang "membungkus" fungsi lain untuk menambah fitur.

Contoh: Caching Decorator

javascript
function lambat(x) {
  // Simulasi operasi berat
  console.log(`Menghitung ${x}...`);
  return x * x;
}

function cachingDecorator(fn) {
  const cache = new Map();

  return function(x) {
    if (cache.has(x)) {
      console.log(`Dari cache: ${x}`);
      return cache.get(x);
    }

    const hasil = fn(x); // panggil fungsi asli
    cache.set(x, hasil);
    return hasil;
  };
}

const cepat = cachingDecorator(lambat);

console.log(cepat(5)); // "Menghitung 5..." → 25
console.log(cepat(5)); // "Dari cache: 5" → 25 (instan!)
console.log(cepat(3)); // "Menghitung 3..." → 9

func.call() — Panggil dengan this Tertentu

javascript
function sapa() {
  console.log(`Hai, ${this.nama}`);
}

const user1 = { nama: "Andi" };
const user2 = { nama: "Budi" };

sapa.call(user1); // "Hai, Andi"
sapa.call(user2); // "Hai, Budi"

// Dengan argumen:
function info(kota, umur) {
  console.log(`${this.nama} dari ${kota}, umur ${umur}`);
}

info.call(user1, "Jakarta", 25); // "Andi dari Jakarta, umur 25"

func.apply() — Sama tapi Argumen dalam Array

javascript
// call: argumen satu-satu
info.call(user1, "Jakarta", 25);

// apply: argumen dalam array
info.apply(user1, ["Jakarta", 25]);

// Hasilnya sama! Bedanya cuma cara passing argumen

Decorator untuk Method (Pakai call)

javascript
const worker = {
  nama: "Server",
  proses(x) {
    console.log(`${this.nama} memproses ${x}`);
    return x * 2;
  }
};

function cachingDecorator(fn) {
  const cache = new Map();
  return function(x) {
    if (cache.has(x)) return cache.get(x);
    const hasil = fn.call(this, x); // forward this!
    cache.set(x, hasil);
    return hasil;
  };
}

worker.proses = cachingDecorator(worker.proses);
worker.proses(5); // "Server memproses 5" → 10
worker.proses(5); // dari cache → 10

Forwarding Semua Argumen

javascript
function decorator(fn) {
  return function() {
    // Forward semua argumen + this
    return fn.apply(this, arguments);
  };
}

// Versi modern dengan rest/spread:
function decoratorModern(fn) {
  return function(...args) {
    return fn.call(this, ...args);
  };
}
⚠️Jebakan!
  • Decorator mengganti referensi fungsi — property asli hilang
  • arguments bukan array sungguhan (tidak punya method array)
  • Hati-hati dengan this saat membungkus method
🎯Challenge

Buat delayDecorator(fn, ms) yang menunda eksekusi fungsi selama ms milidetik:

javascript
function sapa(nama) {
  console.log(`Hai, ${nama}`);
}

const sapaLambat = delayDecorator(sapa, 2000);
sapaLambat("Andi"); // Setelah 2 detik: "Hai, Andi"

5.10 Function Binding

Masalah: Kehilangan this

Analogi: Kamu kasih nomor telepon teman ke orang lain. Orang itu telepon, tapi yang angkat bukan temanmu — salah sambung! Itulah yang terjadi saat method "dilepas" dari objeknya.

javascript
const user = {
  nama: "Andi",
  sapa() {
    console.log(`Hai, saya ${this.nama}`);
  }
};

// Method dilepas dari objek
const fungsiSapa = user.sapa;
fungsiSapa(); // "Hai, saya undefined" 😱

// Atau di setTimeout
setTimeout(user.sapa, 1000); // "Hai, saya undefined" 😱

Solusi 1: Wrapper Function

javascript
setTimeout(function() {
  user.sapa(); // "Hai, saya Andi" ✅
}, 1000);

// Atau arrow function (lebih singkat):
setTimeout(() => user.sapa(), 1000); // ✅

Kelemahan: Kalau user berubah sebelum timeout selesai, wrapper akan panggil objek yang baru.

Solusi 2: bind() — Kunci this Permanen

javascript
const sapaTerikat = user.sapa.bind(user);

sapaTerikat(); // "Hai, saya Andi" ✅
setTimeout(sapaTerikat, 1000); // "Hai, saya Andi" ✅

// Bahkan kalau user berubah:
user = { nama: "Budi" };
sapaTerikat(); // "Hai, saya Andi" ← tetap Andi! bind sudah kunci

bind dengan Argumen (Partial Application)

javascript
function kali(a, b) {
  return a * b;
}

// Kunci argumen pertama = 2
const kaliDua = kali.bind(null, 2);

console.log(kaliDua(3)); // 6 (= 2 * 3)
console.log(kaliDua(5)); // 10 (= 2 * 5)

// Kunci argumen pertama = 3
const kaliTiga = kali.bind(null, 3);
console.log(kaliTiga(4)); // 12 (= 3 * 4)

Bind Semua Method Sekaligus

javascript
const user = {
  nama: "Andi",
  sapa() { console.log(`Hai, ${this.nama}`); },
  bye() { console.log(`Bye, ${this.nama}`); }
};

// Bind semua method
for (const key in user) {
  if (typeof user[key] === "function") {
    user[key] = user[key].bind(user);
  }
}

const { sapa, bye } = user; // destructure aman!
sapa(); // "Hai, Andi" ✅
bye();  // "Bye, Andi" ✅
⚠️Jebakan!
  • bind membuat fungsi BARU — tidak mengubah yang asli
  • Fungsi yang sudah di-bind tidak bisa di-bind ulang (this sudah terkunci)
  • bind menghilangkan custom property dari fungsi asli
🎯Challenge

Buat fungsi partial(fn, ...argsTetap) yang mirip bind tapi TANPA mengunci this:

javascript
const user = {
  nama: "Andi",
  sapa(waktu, pesan) {
    console.log(`[${waktu}] ${this.nama}: ${pesan}`);
  }
};

user.sapaPagi = partial(user.sapa, "08:00");
user.sapaPagi("Selamat pagi!"); // "[08:00] Andi: Selamat pagi!"

5.11 Arrow Function (Revisited)

Bukan Sekadar Sintaks Pendek

Arrow function bukan cuma cara singkat menulis fungsi — mereka punya perilaku berbeda yang fundamental.

Tidak Punya this Sendiri

Arrow function mengambil this dari luar (lexical this):

javascript
const tim = {
  nama: "Frontend",
  anggota: ["Andi", "Budi", "Cici"],

  tampilkan() {
    // Arrow function mengambil this dari tampilkan()
    this.anggota.forEach(anggota => {
      console.log(`${anggota} - Tim ${this.nama}`);
    });
  }
};

tim.tampilkan();
// "Andi - Tim Frontend"
// "Budi - Tim Frontend"
// "Cici - Tim Frontend"

// Kalau pakai function biasa:
const tim2 = {
  nama: "Backend",
  anggota: ["Dedi", "Eka"],

  tampilkan() {
    this.anggota.forEach(function(anggota) {
      console.log(`${anggota} - Tim ${this.nama}`); // ❌ this = undefined!
    });
  }
};

Tidak Punya arguments

javascript
function biasa() {
  console.log(arguments); // ✅ [1, 2, 3]
}
biasa(1, 2, 3);

const arrow = () => {
  // console.log(arguments); // ❌ Error! Tidak ada arguments
};

// Solusi: pakai rest parameter
const arrowFix = (...args) => {
  console.log(args); // ✅ [1, 2, 3]
};
arrowFix(1, 2, 3);

Tidak Bisa Jadi Constructor

javascript
const Orang = (nama) => {
  this.nama = nama;
};

// new Orang("Andi"); // ❌ TypeError: Orang is not a constructor

Tidak Punya super

Arrow function mengambil super dari fungsi luar (berguna di class inheritance — nanti di bab Class).

Kapan Pakai Arrow vs Function Biasa?

SituasiPakai
Callback pendek (map, filter, forEach)Arrow ✅
Method di objekFunction biasa ✅
ConstructorFunction biasa ✅
Event handler yang butuh this = elementFunction biasa ✅
Butuh argumentsFunction biasa ✅
⚠️Jebakan!
  • Jangan pakai arrow untuk method objek:
javascript
const user = {
  nama: "Andi",
  // ❌ SALAH — arrow tidak punya this sendiri
  sapa: () => {
    console.log(`Hai, ${this.nama}`); // undefined!
  },
  // ✅ BENAR
  sapa2() {
    console.log(`Hai, ${this.nama}`); // "Andi"
  }
};
  • Arrow function tidak bisa di-bind, call, atau apply untuk mengubah this — karena memang tidak punya this sendiri
🎯Challenge

Refactor kode ini agar bekerja dengan benar (gunakan pengetahuan tentang arrow function):

javascript
const kalkulator = {
  nilai: 0,
  tambah(angka) {
    // Bug: setTimeout kehilangan this
    setTimeout(function() {
      this.nilai += angka;
      console.log(`Nilai sekarang: ${this.nilai}`);
    }, 1000);
  }
};

kalkulator.tambah(5); // Harusnya: "Nilai sekarang: 5"

Ringkasan Bab 5

KonsepInti
RekursiFungsi panggil diri sendiri, butuh base case
Rest/Spread... mengumpulkan (rest) atau membongkar (spread)
ClosureFungsi "ingat" variabel dari tempat dia dibuat
varFunction scope, hoisting — hindari di kode baru
Objek GlobalglobalThis — wadah variabel global
Function ObjectFungsi = objek, punya .name, .length
new FunctionBuat fungsi dari string — tanpa closure
setTimeout/setIntervalJadwalkan eksekusi di masa depan
DecoratorBungkus fungsi untuk tambah fitur
bindKunci this permanen + partial application
Arrow FunctionTanpa this, arguments, super sendiri

Pesan penting: Closure dan this adalah dua konsep yang paling sering bikin bingung di JavaScript. Kalau kamu paham bab ini, kamu sudah melewati rintangan terbesar! 🎯

Sudah paham materi ini?

Tandai sebagai selesai untuk melacak progress-mu.