Javascript Module System

Pada bagian ini kita akan membahas proses melakukan organisasi kode program. Ketika kita mengembangkan sebuah aplikasi kecil sederhana, organisasi kode program bukan merupakan masalah. Aplikasi yang sederhana, yang katakanlah hanya terdiri dari beberapa ratus baris kode, dapat disimpan di dalam sebuah file dan masih tetap dapat dirawat dengan mudah. Namun seiring berkembangnya aplikasi, seringkali kita akan mencapai satu titik di mana struktur program dan fungsi-fungsi di dalamnya menjadi sulit dirawat (unmaintainable) dan bahkan sulit dimengerti. Kode program yang awalnya mudah dimengerti dengan fungsi-fungsi yang terfokus dan terpisah dengan baik menjadi kumpulan sayuran yang menyerupai gado-gado: saling bercampur dan tidak kentara kegunaan dari masing-masing komponennya.

Pada bahasa pemrograman lain, untuk menanggulangi hal ini kita melakukan dua hal:

  1. Memecah-mecah kode program menjadi banyak bagian-bagian yang lebih kecil. Biasanya bagian kecil dari kode ini disebut dengan nama modul. Setiap kode modul memiliki peranan spesifik, dan biasanya sebuah modul terdiri dari beberapa fungsi. Semakin spesifik dan sejenis fungsi yang terkumpul di dalam sebuah modul semakin baik. Tingkat kecocokan fungsi-fungsi di dalam modul ini kita kenal dengan istilah cohesion.
  2. Setelah kode program dipecah menjadi bagian-bagian kecil, masing-masing komponen kecil ini kemudian dapat kita hubungkan untuk menjadi sebuah komponen yang lebih besar atau bahkan program jadi.

Pembuatan modul dari kode program dapat dilakukan dengan banyak cara, seperti mengumpulkan fungsi-fungsi ke dalam kelas atau objek, dan kmeudian memasukkan kode tersebut ke dalam sebuah file. Bagian kedua, di mana kita dapat memanggil modul-modul lain dengan mudah, biasanya didukung secara langsung oleh bahasa pemrograman yang digunakan. Contohnya, pada bahasa Java kita memiliki perintah import dan pada C# kita memiliki perintah using. Javascript, sampai pada saat penulisan (November 2014), sayangnya belum mengimplemenatsikan fitur ini secara alami di dalam bahasa (meskipun telah direncanakan untuk ke depannya).

Karena belum diimplementasikan secara langsung oleh bahasa pemrogramannya, situasi sistem manajemen modul pada Javascript belum terlalu baik. Terdapat beberapa alternatif implementasi, yang berbeda-beda dan memiliki kelebihan dan kekurangannya masing-masing. Kita akan mencoba melihat dan membahas beberapa sistem yang paling populer pada bagian ini, yaitu AMD, CommonJS, dan terakhir ES6 Harmony untuk melihat bagaimana Javascript akan mengimplementasikan sistem manajemen modul kedepannya.

Persiapan Module System: Script Loader

Sebelum mulai membahas tentang module system, kita akan terlebih dahulu membahas sebuah sistem lain yang akan selalu digunakan oleh module system: script loader. Script loader merupakan sebuah sistem yang membantu kita dalam melakukan pengambilan file-file kode yang dibutuhkan oleh module system.

Karena terdapat banyak jenis dari script loader, masing-masing dengan kelebihan dan kekurangannya sendiri, kita tidak akan membahas hal ini terlalu dalam. Script loader yang akan kita gunakan adalah RequireJS.

AMD (Asynchronous Module Definition)

Tujuan dari pembuatan AMD adalah untuk menyediakan sebuah solusi dalam mengembangkan kode modular Javascript. AMD dirancang agar kita dapat mendefinisikan dan mengunakan modul secara asinkron. Sifat asinkron serta fleksibilitas dari AMD membuat AMD banyak disukai oleh pengembang, dan dianggap sebagai standar implementasi module system yang cukup baik sebelum ES Harmony siap digunakan.

Module System pada AMD

Terdapat dua method utama yang perlu kita mengerti sebelu menggunakan AMD, yaitu:

  1. define digunakan untuk membuat sebuah model baru.
  2. require digunakan untuk menggunakan modul yang telah didefinisikan, menjadikan modul kita sekarang bergantung kepada modul lain.

Fungsi define dapat digunakan dengan cukup sederhana:

1
2
3
4
5
define(
    module_id /*optional*/,
    [dependencies] /*optional*/,
    definition function /*function for instantiating the module or object*/
);

Seperti yang dapat dilihat dari kode di atas, bagian module_id bersifat opsional, dan biasanya hanya digunakan ketika kita menggunakan sistem lain yang tidak mendukung AMD. Jika module_id tidak diisikan, modul yang kita gunakan disebut dengan modul anonim. Jika modul kita bersifat anonim, maka nama dari modul akan dikenali sebagai nama file oleh AMD. Hal ini memungkinkan kita untuk mengubah lokasi modul dengan bebas, tanpa harus terikat dengan bagaimana kita menyimpan modul tersebut.

Parameter kedua, [dependencies], juga bersifat opsional. Parameter ini merupakan sebuah array dari modul-modul lain yang akan digunakan di dalam modul yang akan dibuat. Karena seringkali kita akan membuat modul yang tidak bergantung dengan modul-modul lain, maka modul ini bersifat opsional.

Argumen ketiga (definition function) merupakan fungsi yang nantinya dijalankan untuk menghasilkan modul baru kita. Bayangkan fungsi ini sebagai constructor dari objek yang akan kita gunakan nantinya.

Berikut adalah contoh pembuatan sebuah modul sederhana:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
define('myModule',
       ['foo', 'bar'],
       // fungsi definisi modul
       // perhatikan bahwa foo dan bar, dependensi dari modul,
       // dijadikan parameter fungsi di sini.
        function ( foo, bar ) {
            // modul ini akan dikembalikan nantinya
            // agar dapat digunakan oleh pengguna modul lain
            var myModule = {
                doStuff:function(){
                    console.log('Yay! Stuff');
                }
            }

            return myModule;
        }
);

Penggunaan modul sendiri dapat dilakukan dengan cukup sederhana:

1
2
3
4
5
require(['foo', 'bar'], function ( foo, bar ) {
    // kode di sini

    foo.doSomething();
});

Pada kode di atas, kita dapat menganggap foo dan bar sebagai sebuah fungsi eksternal. Untuk menggunakan kedua fungsi ini kita memanggilnya melalui parameter pertama dari require, dan kemudian mengirimkan kedua objeknya melalui parameter fungsi. Cara ini merupakan bentuk paling sederhana dari penggunaan modul pada AMD.

CommonJS

CommonJS merupakan standar yang lebih luas dari AMD, di mana ia mencakup tidak hanya module system, tetapi juga paket-paket standar yang ada di dalam Javascript. Kita dapat menyamakan CommonJS dengan pustaka standar pada bahasa seperti Java atau C# (misal: system.out.* pada Java). Hal ini berarti selain cara untuk membuat dan memanggil modul CommonJS juga mencakup berbagai modul standar seperti modul input output, pembacaan file, dll.

Seperti AMD, CommonJS memiliki dua komponen utama untuk melakukan pembuatan / pemanggilan modul:

  1. exports merupakan sebuah objek global yang digunakan untuk menyimpan modul.
  2. require merupakan sebuah fungsi global yang dapat memanggil modul-modul yang ada di dalam exports.

Langsung saja, berikut adalah contoh pembuatan modul pada CommonJS:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// definisi fungsi atau objek atau modul
// yang ingin kita buat
function foobar(){
    this.foo = function(){
        console.log('Hello foo');
    };

    this.bar = function(){
        console.log('Hello bar');
    };
};

// simpan agar fungsi foobar dapat digunakan
// di dalam modul lain
exports.foobar = foobar;

Seperti yang dapat dilihat pada kode di atas, pada CommonJS kita cukup hanya menyimpan fungsi atau objek yang kita ingin gunakan pada modul lain ke dalam exports. exports sendiri bersifat global, yang artinya dapat kita panggil kapanpun, di manapun.

Untuk menggunakan fungsi foobar di atas kita dapat memanggil fungsi require seperti berikut:

1
2
3
4
var foobar = require('./foobar').foobar,
test = new foobar();

test.bar(); // 'Hello bar'

Mirip seperti AMD, ketika kita memanggil modul dari CommonJS, pemanggilan dilakukan melalui file, relatif terhadap modul yang sedang mengakses sekarang. Jika dianalogikan, require('./foobar') pada kode di atas dapat dianggap mengakses objek global exports yang ada pada file foobar.js.

Perbandingan AMD dan CommonJS

Sekilas CommonJS memang terlihat lebih sederhana untuk digunakan. Sayangnya, terdapat banyak pengembang yang berpendapat bahwa CommonJS lebih cocok digunakan hanya pada sisi server. Pendapat seperti ini didasarkan pada berbagai modul bawaan pada CommonJS yang dianggap lebih condong ke penggunaan Javascript pada sisi server misalnya I/O, system (pemanggilan program, pembuatan proses, dkk). Fitur-fitur seperti ini tentu saja mustahil untuk diimplementasikan pada sisi browser tanpa membuka lobang keamanan yang besar. Hal ini kemudian menjadikan CommonJS gagal sebagai stanar de facto untuk module system JavaScript, meskipun CommonJS digunakan secara luas pada pemrograman sisi server seperti NodeJS.

AMD sendiri lebih menggunakan pendekatan yang mengutamakan browser dalam pengembangannya. Fitur-fitur seperti pengambilan modul secara asinkron dan kompatibilitas terhadap Javascript lama menunjukkan hal ini. Tanpa menggunakan I/O file, AMD dapat mendukung berbagai jenis modul, dari pustaka seperti jQuery sampai framework seperti Dojo. Semua jenis modul AMD juga dapat dipastikan berjalan secara alami (native) dalam browser. Hal ini membuat AMD menjadi sangat fleksibel.

Pendekatan module system dari ES Harmony sendiri sebenarnya lebih dekat dengan CommonJS dalam beberapa hal seperti cara impor / ekspor modul dibandingkan dengan AMD. Hal ini dikarenakan ES Harmony yang mencoba untuk mengikutsertakan penggunaan Javascript pada server dan klien sebagai kasus penggunaan module systemnya. Tanpa adanya fungsi standar yang berhubungan dengan server, ES Harmony juga mendukung berbagai fitur baik dari AMD seperti penggunaan modul asinkron.

Secara singkat, beberapa kelebihan utama dari AMD yaitu:

  • Pendekatan modul yang berjalan secara native di browser.
  • Fleksibel dan cukup sederhana untuk digunakan.
  • Definisi modul dibungkus dengan baik, tanpa harus memenuhi (mengotori) objek global.
  • Dapat bekerja secara asinkron.
  • Tidak bergantung kepada sisi server.
  • Karena bersifat asinkron, kita dapat melakukan lazy loading jika diperlukan.

Adapun kelebihan utama dari CommonJS adalah:

  • Memiliki banyak fungsi bawaan yang berguna.
  • Karena menggunakan sisi server, terdapat banyak fitur yang tidak mungkin dimiliki implementasi lain.
  • Cara kerjanya lebih dekat dengan ES Harmony.
  • Lebih sederhana digunakan dibandingkan dengan AMD (masih dapat diperdebatkan).

ES6 Harmony

Setelah melihat bagaimana AMD dan CommonJS mencoba menyelesaikan permasalahan module system pada Javascript, sekarang kita akan melihat bagaimana Javascript berencana untuk menyelesaikan masalah ini. Pada ES Harmony, konsep dasar yang diguanakan untuk menyelesaikan masalah module system masih sama:

  1. Perintah import mengikatkan nilai export dari sebuah modul untuk digunakan secara lokal.
  2. Perintah export membuat nilai-nilai yang ada di dalam sebuah modul dapat dibaca (hanya baca, tidak dapat mengubah) oleh modul lain.

Perbedaan utama dari sistem yang disesiakan ES Harmony hanyalah pada integrasi yang lebih ketat karena sifat native-nya. Tidak terdapat perbedaan yang terlalu banyak dibandingkan AMD, CommonJS, dan bahkan bahasa pemrograman lain pada implemenatsi module system ES Harmony. Faktanya, implementasi yang dibuat sangat umum dan sederhana sehingga kebanyakan programmer akan langsung dapat mengerti maksud dan cara penggunaan kode module system ES Harmony, misalnya seperti berikut:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
module staff{
    // spesifikasikan export untuk nilai
    // yang dapat diakses modul lain
    export var baker = {
        bake: function( item ){
            console.log('Woo! I just baked ' + item);
        }
    }
}

module skills{
    export var specialty = "baking";
    export var experience = "5 years";
}

module cakeFactory{

    // hanya import bagian tertentu saja
    import baker from staff;

    // import semuanya
    import * from skills;

    export var oven = {
        makeCupcake: function( toppings ){
            baker.bake('cupcake', toppings);
        },
        makeMuffin: function( mSize ){
            baker.bake('muffin', size);
        }
    }
}

Kita bahkan dapat menggunakan modul yang berada pada server lain dengan ES Harmony, seperti berikut:

1
2
3
module cakeFactory from 'http://addyosmani.com/factory/cakes.js';
cakeFactory.oven.makeCupcake('sprinkles');
cakeFactory.oven.makeMuffin('large');

Penutup

Pada bagian ini kita telah melihat beberapa pilihan moduel system pada Javascript. Module system memiliki kelebihan jika dibandingkan dengan penggunaan fungsi maupun objek global pada Javascript yang biasa kita gunakan. Keuntungan utamanya yang didapatkan misalnya kita tidak lagi perlu mengkhawatirkan urutan pemasukan file pada tag <script> yang digunakan.

Pembaca sangat direkomendasikan untuk setidaknya mencoba salah satu dari CommonJS atau AMD sebelum melanjutkan ke bab berikutnya, karena pada bagian framework kita akan menggunakan module system sepenuhnya.

comments powered by Disqus
Kembali ke bertzzie.com