Immediately-Invoked Function Expression (IIFE) dan Motivasinya

Penggunaan variabel global dalam Javascript sangat ditentang karena banyak alasan [1]. Dua yang utama adalah sulitnya mengontrol akses terhadap resource yang tersedia untuk seluruh aplikasi, yang bisa mengakibatkan inkonsistensi data pada skala besar; dan penggunaan variabel global menyebabkan polusi terhadap namespace, yang bisa menyebabkan penggantian nilai tanpa terduga akibat kesamaan nama variabel. Karena itu, developer Javascript dituntut untuk pandai mengelola scope kode mereka agar sebisa mungkin tidak menggunakan global scope untuk menyimpan data.

Salah satu pendekatan dalam mengurangi penggunaan variabel global adalah dengan menggunakan pola Immediately-Invoked Function Expression. Dengan pola ini, kita dapat memanggil suatu fungsi tanpa harus menyimpannya, dan dapat 'menyembunyikan' kode yang kompleks beserta variabel-variabel di dalamnya yang berpotensi mengotori global scope Javascript aplikasi Anda jika diekspos. Untuk dapat memahami pola tersebut secara menyeluruh, pertama-tama kira perlu mengulas tentang fungsi itu sendiri.

Function Declaration vs Function Expression

Dalam Javascript ketika kita menemukan kata kunci function, maka ada dua kemungkinan: yang kita temui merupakan function declaration, atau function expression.

Function declaration adalah bagian dari kode yang bertindak seperti deklarasi variabel (var, let) untuk suatu fungsi. Juga seperti deklarasi variabel, deklarasi fungsi juga terdampak hoisting [1:1]. Rule of thumb dalam mengidentifikasi suatu function declaration adalah ia selalu dimulai dengan kata function. Jika tidak, maka ia termasuk dalam kemungkinan kedua, yaitu function expression [1:2]. Function declaration hanya dapat ditempatkan di program level (misalnya global scope) atau dalam function block lainnya.

Function expression adalah sebuah fungsi yang bertindak/memiliki posisi sebagai expression, yaitu bagian dari kode yang menghasilkan suatu nilai [1:3]. Selain penempatannya, yang membedakan function expression dengan function declaration adalah penamaannya: penamaan function expression opsional, lain dengan function declaration yang harus memiliki nama.

// function declaration
function foo() {
  // ...
}

// function expression
var foo = function() {
  // ...
}

Penggunaan Function Block untuk Mengontrol Privasi Kode

Karena scoping dalam Javascript disusun atas blok-blok yang dibuat oleh fungsi, maka penggunaan fungsi untuk melokalisasi data menjadi krusial. Ketika kita mendeklarasikan variabel ataupun fungsi di dalam suatu function block, maka mereka hanya akan dapat diakses di dalam blok fungsi tersebut.

function foo() {
  var bar = 13;
  function baz() {
    console.log(bar);
  }
  baz();
}

foo(); // 13
console.log(bar); // ReferenceError: bar is not defined
baz(); // ReferenceError: baz is not defined

Dalam contoh ini, bar dan baz menjadi variabel "privat" dalam fungsi foo yang tidak mengotori global scope. Sekarang bayangkan ketika kita hanya perlu fungsi yang dirujuk oleh foo untuk dieksekusi sekali dalam aplikasi kita, tentu akan sangat mahal jika kita harus menyimpannya ke dalam satu variabel global. Bisakah kita membuatnya tereksekusi tanpa harus menyimpan referensinya dalam suatu variabel?

Memperkenalkan Immediately-Invoked Function Expression [1:4]

Flashback ke bagian declaration vs expression, diketahui bahwa function foo() { //.. } sebagai function declaration hanya bertugas mendeklarasikan nama foo yang mereferensikan sebuah fungsi. Kemudian seperti yang bisa kita pantau dari contoh-contoh kode sebelumnya, () (disebut sebagai parens, singkatan dari parentheses) yang diletakkan di akhir nama sebuah fungsi yang sudah dideklarasikan akan memanggil fungsi tersebut. Berdasarkan logika tersebut, bisakah fungsi tersebut kita keluarkan dan diakhirkan dengan () untuk langsung mengeksekusinya tanpa mengalokasikan variabel untuknya?

function() { /* ... */ }(); // Syntax Error: unexpected token '('

Kita mendapatkan syntax error, karena parser Javascript menganggap function() di depan sebagai function declaration, dan sebuah function declaration harus memiliki nama.

Rule of thumb dalam mengidentifikasi suatu function declaration adalah ia selalu dimulai dengan kata function.

Sekarang, apa yang akan terjadi jika kita berkompromi dan memberikan fungsi tersebut sebuah nama?

function foo() { /* ... */ }(); // Syntax Error: unexpected token ')'

Kita kembali mendapatkan syntax error, tapi dengan alasan berbeda. Kali ini error tersebut terjadi karena function foo() { //... } dianggap sebagai satu statement [1:5] yang kemudian diikuti (). Parens di sini alih-alih berperan sebagai perintah untuk mengeksekusi fungsi, malah bertindak sebagai grouping operator [1:6] yang harus mengapit suatu expression. Ketika kita mematuhinya dan memberikan expression di dalam parens tersebut pun hasilnya tidak sesuai harapan.

function foo() { /* .. */ }(1);
/* tidak menghasilkan error, tetapi tidak terjadi apa-apa
/* selain foo terdeklarasi
**/

Hal ini dikarenakan function foo() { //.. } dan (1) dianggap sebagai dua statement yang, meskipun valid, tidak terkait satu sama lain. Hanya sebuah function declaration yang diikuti grouping operator tidak bermakna.

Untuk membuat parser memperlakukan () di akhir sebagai perintah mengeksekusi fungsi, kita perlu membuat fungsi yang mendahuluinya menjadi sebuah function expression. Caranya sederhana, cukup memasukkannya ke dalam parens. Inilah yang dinamakan sebagai Immediately-Invoked Function Expression (dibaca "iffy") oleh Ben Alman, sebuah function expression yang langsung dipanggil saat pembuatannya.

(function(){ console.log(3) })(); // 3
(function(){ console.log(3) }()); // 3 -- gaya Crockford

Karena parens yang mengurung function declaration hanya bertugas untuk mengonversi deklarasi tersebut menjadi function expression, kita dapat mengabaikannya ketika parser sudah mengetahui bahwa statement yang mendahului parens kedua adalah suatu expression.

var foo = function() { /* ... */ }();
true && function() { /* ... */ }();

Meskipun begitu, Ben Alman menyarankan untuk tetap menggunakan parens tersebut untuk meningkatkan readability dari kode kita, karena akan memperjelas maksud dari expression tersebut adalah sebuah IIFE.

Selain parens, ada beberapa trik lain yang bisa digunakan untuk "menyulap" function declaration Anda menjadi function expression. Trik-trik ini bisa menghemat satu byte, dengan mengorbankan nilai kembalian dan readability [1:7].

!function() { /* ... */ }(); // true
~function() { /* ... */ }(); // -1
+function() { /* ... */ }(); // NaN
-function() { /* ... */ }(); // NaN

Dalam menggunakan IIFE, meskipun kita dapat memakai anonymous function, Kyle Simpson [1:8] menyarankan agar kita tetap memberikan nama kepada fungsi yang kita panggil. Hal ini selain bertujuan meningkatkan readability, juga sangat membantu dalam proses debugging, terutama ketika aplikasi kita sudah berskala sangat besar.

Selain dapat mengurangi polusi global scope secara dramatis, IIFE juga meningkatkan readability dari kode kita secara umum dan meningkatkan performa program Javascript kita. Selain itu, IIFE sangat membantu dalam pembuatan modul, sebuah kumpulan fungsi yang dikelompokkan berdasarkan logika bisnis, yang saling berinteraksi dan menjadi bagian dari suatu aplikasi.


Tulisan ini adalah rangkuman dari ulasan Ben Alman tentang IIFE di blognya, serta beberapa sumber lain yang penulis anggap relevan. Semoga bermanfaat.



  1. Kyle Simpson adalah trainer Javascript di Frontend Masters dan penulis serial You Don't Know JS. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎