Ruang Lingkup Variabel Javascript ES6 (Bagian 1)


Daftar isi:

  1. var Declarations and Hoisting
  2. Block-Level Declarations
    • let Declarations
    • No Redeclaration
    • const Declarations
    • Constants vs. let Declarations
    • Object Declarations with const
  3. The Temporal Dead Zone

Secara tradisional, cara mendeklarasikan variabel telah menjadi salah satu bagian rumit dalam bahasa pemrograman Javascript.

Pada sebagian besar bahasa pemrograman berbasis C, variabel (lebih formal dikenal sebagai bindings, seperti namanya sebuah binding akan mengikat nilai di dalam scope) dibuat di tempat deklarasi terjadi.

Namun, dalam Javascript ini tidak terjadi. Dimana variabel Anda sebenarnya dibuat bergantung pada cara mendeklarasikannya, dan ECMAScript 6 menawarkan opsi untuk membuat cakupan kontrol lebih mudah.

Artikel ini mendemonstrasikan mengapa deklarasi var tradisional bisa membingungkan, perkenalan block-level bindings di ECMAScript 6, dan kemudian menawarkan beberapa praktik untuk menggunakannya.

1. var Declarations and Hoisting

Deklarasi variabel yang menggunakan var diperlakukan seolah-olah berada di bagian atas fungsinya (atau dalam cakupan global, jika dideklarasikan di luar fungsi) terlepas dari letak deklarasi sebenarnya terjadi; ini disebut hoisting. Berikut ini adalah demonstrasi
tentang apa yang dilakukan hoisting:

function getValue(condition) {
if (condition) {
var value = "blue";
// other code
return value;
} else {
// value exists here with a value of undefined
return null;
}
// value exists here with a value of undefined
}

Jika Anda tidak terbiasa dengan JavaScript, Anda mungkin mengharapkan variabel value yang akan dibuat hanya jika kondisi bernilai true. Faktanya, variabel value dibuat tanpa mempertimbangkan nilai dari kondisi. Di balik layar, JavaScript engine mengubah fungsi getValue menjadi seperti ini:

function getValue(condition) {
var value;
if (condition) {
value = "blue";
// other code
return value;
} else {
return null;
}
}

Deklarasi variabel value diangkat ke atas, dan inisialisasi tetap di tempat yang sama. Artinya nilai variabel masih bisa diakses dari dalam klausa else. Jika diakses dari klausa else, variabel hanya akan memiliki nilai undefined karena belum diinisialisasi di blok else.

Sering kali developer JavaScript baru membutuhkan waktu untuk terbiasa dengan deklarasi hoisting, dan kesalahpahaman tentang perilaku unik ini bisa menyebabkan bug. Untuk alasan ini, ECMAScript 6 memperkenalkan block-level scoping untuk memberi developer lebih banyak kontrol atas siklus hidup variabel.

2. Block-Level Declarations

Block-Level Declarations mendeklarasikan binding yang tidak dapat diakses di luar block scope tertentu. Block scope, juga disebut lexical scopes, dibuat di tempat-tempat berikut:

  • Dalam sebuah fungsi
  • dalam sebuah blok

Block scope adalah cara kerja di banyak bahasa berbasis C, dan pengenalan block-level declarations di ECMAScript 6 dimaksudkan untuk memberikan fleksibilitas yang sama (dan keseragaman) ke dalam pemrograman JavaScript.

Deklarasi let

Sintaks deklarasi let sama seperti deklarasi var. Pada dasarnya Anda dapat mengganti var dengan let untuk mendeklarasikan variabel tetapi membatasi cakupan variabel hanya ke blok kode saat ini (ada beberapa perbedaan kecil lainnya, yang dibahas dalam “The Temporal Dead Zone” di bagian akhir artikel ini).

Karena deklarasi let tidak diangkat ke atas blok yang melingkupi, yang terbaik adalah menempatkan deklarasi let terlebih dahulu di blok sehingga deklarasi tersedia untuk seluruh blok. Berikut contohnya:

function getValue(condition) {
if (condition) {
let value = "blue";
// other code
return value;
} else {
// value doesn't exist here
return null;
}
// value doesn't exist here
}

Fungsi getValue di sini berperilaku lebih mirip dengan bahasa berbasis C lainnya. Karena nilai variabel dideklarasikan menggunakan let, bukan var, deklarasi tidak diangkat ke bagian atas definisi fungsi, dan nilai variabel tidak lagi dapat diakses setelah eksekusi mengalir keluar dari blok if. Jika kondisi bernilai false, maka nilai tidak pernah dideklarasikan atau diinisialisasi.

No Redeclaration

Tidak ada deklarasi ulang. Jika identifier telah didefinisikan dalam suatu lingkup, menggunakan identifier dengan deklarasi let di dalam lingkup itu akan menyebabkan error. Berikut contohnya:

var count = 30;
// throws an error
let count = 40;

Pada contoh di atas, count dideklarasikan dua kali: sekali dengan var dan sekali dengan let.

Karena let tidak akan mendefinisikan ulang identifier yang sudah ada dalam lingkup yang sama, deklarasi let akan memunculkan error.

Sebaliknya, tidak ada error yang muncul jika deklarasi let membuat variabel baru dengan nama yang sama dengan variabel dalam lingkupnya, seperti yang ditunjukkan dalam kode berikut:

var count = 30;
if (condition) {
// doesn't throw an error
let count = 40;
// more code
}

Deklarasi let ini tidak menimbulkan error karena membuat variabel baru yang disebut count dalam blok if alih-alih membuat count di blok global. Dalam blok if, variabel baru ini menyembunyikan count global, mencegah akses ke sana hingga eksekusi keluar dari blok if.

const Declarations

Anda juga dapat mendefinisikan binding di ECMAScript 6 dengan sintaks deklarasi const. Binding yang dideklarasikan menggunakan const dianggap konstanta, yang berarti nilainya tidak dapat diubah setelah diinisialisasi.

Untuk alasan ini, setiap binding const harus diinisialisasi saat deklarasi, seperti yang ditunjukkan dalam contoh ini:

// valid constant
const maxItems = 30;
// syntax error: missing initialization
const name;

Binding maxItems diinisialisasi, sehingga deklarasi const-nya akan berfungsi tanpa masalah. Namun, binding name akan menyebabkan kesalahan sintaks jika Anda mencoba menjalankan program yang berisi kode ini karena name tidak diinisialisasi.

Const versus let Declarations

Const, seperti deklarasi let, adalah block-level declarations. Itu berarti const tidak lagi dapat diakses setelah eksekusi mengalir keluar dari blok tempat mereka dideklarasikan, dan deklarasi tidak diangkat, seperti yang ditunjukkan dalam contoh ini:

if (condition) {
const maxItems = 5;
// more code
}
// maxItems isn't accessible here

Dalam kode ini, konstanta maxItems dideklarasikan dalam blok if. Setelah pernyataan selesai dijalankan, maxItems tidak dapat diakses di luar blok itu.

Dalam kesamaan lain dengan let, deklarasi const melontarkan error saat dibuat dengan identifier untuk variabel yang sudah didefiniskan dalam lingkup yang sama baik variabel itu dideklarasikan menggunakan var (untuk global atau function scope) atau let (untuk block scope). Misalnya, perhatikan kode ini:

var message = "Hello!";
let age = 25;
// each of these throws an error
const message = "Goodbye!";
const age = 30;

Dua deklarasi const akan valid jika dideklarasikan tersendiri, tetapi mengingat sebelumnya terdapat deklarasi var dan let dalam kasus ini, kedua deklarasi const itu adalah sintaks error.

Terlepas dari kesamaan tersebut, ada satu perbedaan signifikan antara let dan const. Menetapkan sebuah const ke konstanta yang telah didefinisikan sebelumnya akan menimbulkan error dalam mode ketat dan non-ketat:

const maxItems = 5;
// throws an error
maxItems = 6;

Sama seperti konstanta dalam bahasa lain, variabel maxItems tidak dapat diberi nilai baru nantinya. Namun, tidak seperti konstanta dalam bahasa lain, nilai yang ada dalam konstanta dapat dimodifikasi jika berupa objek.

Object Declarations mengunakan const

Deklarasi const mencegah modifikasi pada binding-nya, bukan pada nilainya. Artinya, deklarasi const untuk objek tidak mencegah modifikasi objek tersebut. Sebagai contoh:

const person = {
name: "Nicholas"
};
// works
person.name = "Greg";
// throws an error
person = {
name: "Greg"
};

Di sini, binding person dibuat dengan nilai awal sebuah objek
dengan satu properti.

person.name dapat diubah tanpa menyebabkan error karena ini mengubah isi person tetapi tidak mengubah nilai yang terikat pada person tersebut.

Saat kode ini mencoba untuk memberikan nilai kepada person (dengan demikian mencoba untuk mengubah binding), error akan terjadi.

Kehalusan dalam cara const bekerja dengan objek ini mudah disalahpahami. Ingatlah bahwa const menolak modifikasi binding, bukan modifikasi nilai binding.

3. The Temporal Dead Zone

Variabel yang dideklarasikan dengan let atau const tidak dapat diakses kecuali setelah baris deklarasinya. Mencoba untuk melakukannya menghasilkan reference error, bahkan saat menggunakan operasi yang biasanya aman, seperti jenis operasi dalam pernyataan if ini:

if (condition) {
console.log(typeof value); // throws an error
let value = "blue";
}

Di sini, variabel value didefinisikan dan diinisialisasi menggunakan let, tetapi pernyataan itu tidak pernah dieksekusi karena baris sebelumnya memunculkan kesalahan.

Masalahnya adalah value ada dalam the temporal dead zone (TDZ). TDZ tidak pernah disebutkan secara eksplisit dalam spesifikasi ECMAScript, tetapi istilah ini sering digunakan untuk menjelaskan mengapa binding let dan const tidak dapat diakses sebelum deklarasi mereka.

Bagian ini membahas beberapa seluk-beluk penempatan deklarasi yang disebabkan oleh TDZ, dan meskipun contoh yang ditunjukkan menggunakan let, perhatikan bahwa informasi yang sama berlaku untuk const.

Saat JavaScript engine memeriksa blok perintah berikutnya dan menemukan deklarasi variabel, ia akan mengangkat deklarasi ke atas fungsi atau cakupan global (untuk var) atau menempatkan deklarasi di TDZ (untuk let dan const).

Setiap upaya untuk mengakses variabel dalam TDZ akan menghasilkan runtime error. Variabel itu hanya dihapus dari TDZ, dan karena itu aman digunakan, setelah eksekusi mengalir ke deklarasi variabel.

Hal ini terjadi setiap kali Anda mencoba menggunakan variabel yang dideklarasikan dengan let atau const sebelum didefinisikan. Seperti yang ditunjukkan contoh sebelumnya, hal ini bahkan berlaku juga untuk operator typeof yang biasanya aman.

Namun, Anda dapat menggunakan typeof pada variabel di luar blok tempat variabel tersebut dideklarasikan tanpa menimbulkan error, meskipun mungkin tidak memberikan hasil yang Anda cari.
Perhatikan kode ini:

console.log(typeof value); // "undefined"
if (condition) {
let value = "blue";
}

variabel value tidak ada di TDZ saat operasi typeof dijalankan
karena terjadi di luar blok tempat value dideklarasikan. Dalam hal ini tidak ada binding value, dan typeof hanya mengembalikan “undefined”.

TDZ hanyalah salah satu aspek unik dari block bindings. Aspek unik lainnya berkaitan dengan penggunaannya di dalam loop.

referensi: Nicholas C. Zakas. 2016. Understanding Ecmascript 6

1 komentar pada “Ruang Lingkup Variabel Javascript ES6 (Bagian 1)”

Tinggalkan komentar