15 Desember 2021

Lingkup variabel, closure

Javascript adalah bahasa yang berorientasi-fungsi. Itu memberikan kita banyak kebebasan. Sebuah fungsi bisa dibuat kapanpun, diberikan sebagai argumen kedalam fungsi lain, dan lalu dipanggil dari kode yang benar-benar berbeda nanti.

Kita sudah tahu bahwa sebuah fungsi bisa mengakses variabel diluar dari fungsi tersebut (variabel “luar”).

Tapi apa yang terjadi jika variabel luar berubah saat fungsinya dibuat? Akankan fungsinya mendapatkan nilai yang baru atau yang lama?

Dan bagaimana jika sebuah fungsi diberikan sebagai paramter dan dipanggil dibagian kode lain, akankah itu mendapatkan akses ke variabel luar ditempat itu?

Ayo kita peruas pengetahuan kita untuk mengerti skenario ini dan skenario yang lebih kompleks.

Kita akan bahas tentang variabel let/const di sini

Di JavaScript, ada 3 cara mendeklarasi variabel: let, const (cara-cara modern), dan var (sisa masa lalu).

  • Di artikel ini kita akan memakai variabel let dalam contoh.
  • Variabel, yang dideklarasi dengan const, bertindak sama, jadi artikel ini juga tentang const.
  • var usang punya perbedaan mencolok, mereka akan dibahas di artikel Si Tua "var".

Blok kode

Jika variabel dideklarasi di dalam blok kode {...}, ia hanya terlihat di dalam blok itu.

Misalnya:

{
  // lakukan pekerjaan dengan variabel lokal yang harusnya tak terlihat dari luar

  let message = "Hello"; // hanya terlihat dalam blok ini

  alert(message); // Hello
}

alert(message); // Error: message is not defined

Kita bisa memakai ini untuk mengisolasi potongan kode yang melakukan tugasnya sendiri, dengan variabel yang dia punya sendiri:

{
  // tampilkan pesan
  let message = "Hello";
  alert(message);
}

{
  // tampilkan pesan lain
  let message = "Goodbye";
  alert(message);
}
Akan muncul galat tanpa blok

Tolong ingat, tanpa blok terpisah akan muncul galat, jika kita memakai let dengan nama variabel yang sudah ada:

// tampilkan pesan
let message = "Hello";
alert(message);

// tampilkan pesan lain
let message = "Goodbye"; // Galat: variabel sudah dideklarasi
alert(message);

Untuk if, for, while dan lain-lain, variabel yang dideklarasi dalam {...} juga hanya terlihat di situ saja:

if (true) {
  let phrase = "Hello!";

  alert(phrase); // Hello!
}

alert(phrase); // Galat, variabel ini tak ada!

Di sini, setelah if selesai, alert di bawah tak akan melihat phrase, sehingga terjadi galat.

Ini keren, karena ia memperbolehkan kita membuat variabel blok-lokal, yang spesifik ke cabang if.

Hal serupa juga berlaku untuk loop for dan while:

for (let i = 0; i < 3; i++) {
  // variabel i hanya terlihat di dalam for ini
  alert(i); // 0, lalu 1, lalu 2
}

alert(i); // Galat, variabel ini tak ada

Visually, let i is outside of {...}. But the for construct is special here: the variable, declared inside it, is considered a part of the block.

Fungsi bersarang

Sebuah fungsi dikatakan “bersarang” apabila fungsi tersebut dibuat di dalam fungsi lainnya.

Hal tersebut mudah untuk dilakukan di JavaScript.

Kita dapat melakukannya untuk mengatur kode kita, seperti ini:

function sayHiBye(firstName, lastName) {

  // fungsi pembantu untuk digunakan di bawah
  function getFullName() {
    return firstName + " " + lastName;
  }

  alert( "Hello, " + getFullName() );
  alert( "Bye, " + getFullName() );

}

Di sini fungsi bersarang dibuat untuk kemudahan. Fungsi tersebut bisa mengakses variabel luar sehingga dapat mengembalikan nama lengkap. Fungsi bersarang cukup sering ditemui di JavaScript.

Yang lebih menarik yaitu, fungsi bersarang dapat dikembalikan: bisa sebagai properti dari objek baru atau sebagai nilai kembalian itu sendiri. Nilai kembalian tersebut bisa dipakai di tempat lain. Tak peduli di mana, ia masih punya akses ke variabel luar yang sama.

Di bawah ini, makeCounter membuat fungsi “counter” yang mengembalikan angka berikutnya di tiap invokasi:

function makeCounter() {
  let count = 0;

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

let counter = makeCounter();

alert( counter() ); // 0
alert( counter() ); // 1
alert( counter() ); // 2

Meski sederhana, varian kode itu yang sedikit dimodifikasi punya kegunaan praktis, misalnya, sebagai generator angka random untuk menggenerate nilai random untuk tes terotomasi.

How does this work? If we create multiple counters, will they be independent? What’s going on with the variables here?

Memahami hal begini bagus untuk pengetahuan keseluruhan JavaScript dan menguntungkan untuk skenario yang lebih komplex. Jadi ayo kita selami lebih dalam.

Lingkungan Lexikal

Sini jadilah naga!

Penjelasan teknikal mendalam ada di depan.

Semakin jauh aku menghindari detil bahasa level-rendah, pemahaman apapun tanpa mereka akan kekurangan dan tak-lengkap, jadi bersiaplah.

Supaya jelas, penjelasan dibagi dalam beberapa langkah.

Langkah 1. Variabel

Di JavaScript, setiap fungsi yang berjalan, blok kode {...}, dan satu script yang menyeluruh punya objek internal (tersembunyi) yang terasosiasi yang dikenal dengan Lingkungan Lexikal.

Objek Lingkungan Lexikal punya dua bagian:

  1. Rekaman Lingkungan – objek yang menyimpan semua variabel lokal sebagai propertinya (dan beberapa informasi lain seperti nilai this).
  2. Referensi ke lingkungan lexikal luar, yang terasosiasi dengan kode luar.

“Variabel” cuma suatu properti dari objek internal spesial, Rekaman Lingkungan. “Untuk memperoleh atau mengganti variabel” berarti “memperoleh atau mengganti properti dari objek itu”.

Di kode sederhana tanpa fungsi ini, cuma ada satu Lingkugan Lexikal:

Ini yang disebut Lingkungan Lexikal global, terasosiasi dengan script keseluruhan.

Di gambar di atas, kotak persegi panjang artinya Rekaman Lingkungan (simpanan variabel) dan panah artinya referensi luar. Lingkungan Lexikal global tak punya referensi luar, itulah kenapa panahnya menunjuk ke null.

Seiring kodenya mulai bereksekusi dan berjalan, Lingkungan Lexikal berganti.

Ini kode yang sedikit lebih panjang:

Kotak persegi panjang di sisi kanan mendemonstrasikan bagaimana Lingkungan Lexikal global berganti selama exekusi:

  1. Ketika script berjalan, Lingkungan Lexikal di-pre-populasi dengan semua variabel yang terdeklarasi.
    • Awalnya, mereka di state “Belum terinisialisir”. Itu state internal spesial, yang berarti bahwa engine tahu tentang variabelnya, tapi tak akan mengijinkan penggunaan itu sebelum let. Ini hampir sama saja dengan variabel itu tak ada.
  2. Lalu definisi let phrase muncul. Tak ada penetapan dulu, jadi nilainya undefined