Pengembangan Berorientasi Objek pada Javascript

Secara tradisional, pengembangan berorientasi objek (selanjutnya disebut PBO saja) merupakan salah satu teknik rekayasa perangkat lunak yang dirancang untuk menangani kemudahan perawatan program besar. Fitur-fitur yang ditawarkan PBO, dari pembungkusan (encapsulation) sampai ke pewarisan (inheritance) dapat digunakan untuk meningkatkan penghimpunan fokus (separation of concern) dan penggunaan kembali kode (code reuse). Bahasa-bahasa yang memiliki kemampuan PBO biasanya juga memiliki spesifikasi sistem tipe data (type system) yang lengkap dan kompleks. Sistem tipe data ini sangat berguna, terutama untuk memudahkan penulis kode dalam melakukan pergantian tipe (casting). Dengan adanya sistem tipe data yang baik, pengembang program tidak lagi perlu melakukan pergantian tipe secara eksplisit, dan mengorbankan keamanan program.

Javascript, sebagai bahasa yang bersifat duck typed atau loosely typed sangat fleksibel dalam menangani masalah tipe data ini. Hal ini juga lah yang menjadi alasan utama Javascript mengadopsi PBO prototype-based. Dengan menggunakan sistem tipe data fleksibel yang ditambahkan dengan pendekatan PBO yang tidak biasa, Javascript menawarkan pola penggunaan atau pola pemrograman yang berbeda dibandingkan bahasa lain pada umumnya.

Pada bagian ini kita akan melihat beberapa teknik untuk menggunakan objek dengan baik, berfokus pada penggunaan kembali kode. Pembahasan mengenai interaksi antar objek (khususnya design pattern) tidak akan dilakukan, karena hal ini akan memerlukan buku tersendiri.

Pembuatan Objek

Dari bab-bab sebelumnya, kita telah melihat dua cara utama untuk membuat objek. Cara yang pertama adalah dengan menggunakan notasi pembuatan objek:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
var mobil = {
    warna: "",
    bahan_bakar: "",
    kode_mobil: function () {
        return this.warna[0] + this.bahan_bakar[0];
    }
};

var mobilMerah = Object.create(mobil);
mobilMerah.warna = "Merah";
mobilMerah.bahan_bakar = "Bensin";

// menghasilkan
// { warna: 'Merah', bahan_bakar: 'Bensin', kode_mobil: [Function] }
console.log(mobilMerah);

Cara kedua adalah dengan menggunakan closure:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// w dan bb merupakan parameter yang dikirimkan
// ke objek.
// w untuk warna mobil
// bb untuk bahan bakar
var mobil = function (w, bb) {
        var warna = w,
            bahan_bakar = bb,
            kode_mobil = function () {
                return this.warna[0] + this.bahan_bakar[0];
            };

        return {
            warna: warna,
            bahan_bakar: bahan_bakar,
            kode_mobil: kode_mobil
        };
    };

    var mobilMerah = mobil("Merah", "Bensin");

// menghasilkan
// { warna: 'Merah', bahan_bakar: 'Bensin', kode_mobil: [Function] }
console.log(mobil);

Kita lebih menyukai cara kedua, karena pada cara kedua kita memiliki variabel ataupun method privat. Tentu saja kedua cara pembuatan objek ini memiliki kegunaannya masing-masing. Terkadang, jika objek yang dibuat hanya menyimpan data sederhana, kita tidak perlu membuat closure. Selalu pastikan kode yang kita tuliskan adalah kode yang paling sederhana.

Penambahan Fungsionalitas Objek

Sama seperti properti, kita dapat menambahkan method pada sebuah objek turunan dengan cara yang sama seperti menambahkan properti:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
var ayam = {
        sayap: 2,
        kaki: 2
    };

    var burung = Object.create(ayam);
    burung.terbang = function () {
        return "t-t-t-terbang!";
    }

    burung.terbang(); // mencetak "t-t-t-terbang!"

Penambahan fungsionalitas ini tidak hanya dapat dilakukan terhadap objek yang kita buat sendiri saja. Objek-objek dasar seperti Function, Object maupun yang lainnya dapat ditambahkan fungsionalitasnya.

Contohnya, kita dapat menambahkan fungsionalitas baru untuk menambahkan fungsi method ke objek Function agar semua objek bawaan Javascript dapat menambahkan method dengan mudah:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
Function.prototype.method = function (name, func) {
    // pastikan method belum ada sebelumnya.
    if (!this.prototype[name]) {
        this.prototype[name] = func;
        return this;
    }
};

// contoh penggunaan: menambahkan fungsi "bulatkan"
// pada semua bilangan yang ada dalam Javascript.
Number.method("bulatkan", function () {
    return Math.floor(this);
});

var nilai = 10.33;
nilai.bulatkan(); // mencetak 10

Pada contoh di atas, kita menambahkan fungsi ke Function karena objek bawaan Javascript semuanya merupakan turunan dari Function. Hal ini disebabkan oleh kesalahan perancangan Javascript, yang menggunakan Constructor Invocation Pattern untuk semua objek dasarnya. Jika kita mau, tentunya kita dapat langsung menambahkan fungsi method di atas pada Object, agar objek buatan kita juga memiliki fungsi ini.

Pewarisan (Inheritance)

Kemampuan membuat atau menambahkan fungsi objek saja seringkali tidak cukup ketika kita mengembangkan aplikasi. Seringkali kita memerlukan sebuah objek baru yang memiliki fungsionalitas hampir sama dengan objek yang sudah ada. Jika mayoritas dari properti dan method objek lama sama dengan objek baru, tentunya membuat objek baru sangatlah boros.

Untuk menyelesaikan permasalahan di atas, PBO menawarkan konsep pewarisan (inheritance).

Secara umum ide dari pewarisan adalah sebuah objek dapat mewariskan properti maupun method miliknya ke objek lain. Objek yang mendapatkan warisan kemudian akan memiliki method dan properti yang sama, dan bebas mengubah nilai dari properti maupun method tersebut tanpa mempengaruhi nilai objek yang mewariskan. Dengan kemampuan untuk menambah ataupun mengubah inilah kita bisa menyelesaikan masalah objek mirip. Wariskan objeknya, kemudian ganti bagian yang perlu berubah.

Karena kita memiliki dua cara untuk membuat objek, tentunya kita juga akan memiliki dua cara untuk membuat pewarisan:

  1. Ketika kita melakuukan pewarisan terhadap objek secara langsung, kita memanfaatkan fitur pewarisan milik Javascript, yaitu prototype. Pewarisan model ini akan kita sebut sebagai Pewarisan dengan Prototype.
  2. Jika kita ingin melakukan pewarisan terhadap objek yang dibuat dengan closure, kita akan memerlukan cara khusus. Karena closure merupakan konsep pemrograman fungsional, kita akan menamakan cara ini Pewarisan secara Fungsional.

Mari kita lihat kedua cara tersebut.

Pewarisan dengan Prototype

Pewarisan dengan Prototype telah kita bahas sedikit pada bab sebelumnya. Konsep dari pewarisan jenis ini sangat sederhana. Kita memiliki sebuah objek yang sangat berguna. Katakanlah kita memerlukan objek yang hampir sama dengan objek tersebut, dengan perbedaan hanya pada cara kerja satu method saja. Kita dapat membuat turunan objek tersebut secara langsung, dan mengubah method yang cara kerjanya berbeda saja. Tidak perlu ada beberapa tingkat kelas abstrak atau model turunan yang membingungkan.

Misalkan kita memiliki sebuah objek yang sangat berguna:

1
2
3
4
5
6
7
var manusia = {
    nama: "Scott",
    mata: 2,
    buka_mata: function () {
        console.log("Mata membuka maka manusia melihat.");
    }
};

Setelah memiliki objek yang berguna seperti di atas, kita kemudian dapat membuat instan objek yang sama dengan menggunakan Object.create, seperti yang dijelaskan pada bab sebelumnya. Hasil instan objek tersebut kemudian dapat kita ubah sesuai keperluan:

1
2
3
4
5
6
var mutan = Object.create(manusia);
mutan.nama = "Cyclops";
mutan.mata = 1;
mutan.buka_mata = function () {
    console.log("Mutan membuka mata dan yang di depan mata meledak.");
};

Yang kita lakukan di atas lebih dikenal dalam dunia pemrograman sebagai differential inheritance. Intinya, ketika kita membuat objek baru dan mengubahnya, sembari menspesifikasikan perbedaan objek baru dengan objek asalnya.

Pewarisan secara Fungsional

Pewarisan model fungsional memiliki bentuk yang agak berbeda dengan pewarisan pada umumnya. Karena pembuatan objek yang juga berbeda, maka kita akan memerlukan pendekatan yang sedikit berbeda. Sebelum mulai membicarakan tentang cara mewariskan secara fungsional, mari kita lihat kembali dan sempurnakan pembuatan objek secara fungsional.

Ketika membuat objek secara fungsional, pada dasarnya yang kita buat adalah constructor dari objek, yang memanfaatkan closure untuk membuat nilai privat dari objek. Jika kita telaah lebih lanjut, constructor ini memiliki empat tugas utama:

  1. Mendeklarasikan variabel maupun fungsi yang bersifat privat. Seluruh nilai-nilai privat ini dibuat hanya dengan menggunakan var biasa saja.
  2. Membuat objek baru yang nantinya akan dikembalikan oleh fungsi. Pembuatan objek baru dapat dilakukan dengan cara apapun, mulai dari {}, Object.create, ataupun closure.
  3. Tambahkan method ataupun variabel baru untuk objek yang baru dibuat, jika diperlukan. Nilai yang dibuat di sini merupakan nilai publik yang dapat diakses oleh pengguna objek. Seluruh method untuk objek ini dapat mengakses nilai-nilai privat yang telah dibuat pada langkah pertama.
  4. Mengembalikan objek baru yang telah selesai dibuat.

Pada contoh-contoh sebelumnya, langkah dua sampai empat kita jalankan dalam satu perintah, yaitu pada saat pembuatan objek ketika pemanggilan return. Meskipun seringkali hal ini sudah cukup, terkadang kita perlu menuliskan ketiga langkah tersebut secara eksplisit untuk menjaga kerapian kode, terutama jika objek yang dibuat besar dan memiliki banyak fungsionalitas.

Jika menggunakan pola di atas, fungsi constructor kita akan menjadi sedikit lebih rapi, dan dapat dipisahkan dengan baik langkah demi langkah pembuatannya. Pemisahan ini akan memudahkan kita ketika ingin mengubah fungsionalitas objek. Misalkan kita ingin membuat sebuah objek negara:

 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
    var negara = function (data) {
        // langkah 1 (nilai-nilai privat)
        var nama = data.nama || "Negara Kosong",
            penduduk = data.penduduk || 0,
            benua = data.benua || "Benua Kosong",
            dataLengkap = function () {
                return nama + " " + penduduk + " " + benua;
            },
            jumlahPenduduk = function () {
                return penduduk;
            },
            setJumlahPenduduk = function (jumlahBaru) {
                penduduk = jumlahBaru;
            },
            // langkah 2 (membuat objek baru)
            that = {};

        // langkah 3 (tentukan nilai publik)
        that.dataNama = function () {
            return nama;
        };
        that.dataLengkap = dataLengkap;
        that.dataPenduduk = jumlahPenduduk;
        that.setDataPenduduk = setJumlahPenduduk;

        // langkah 4 (kembalikan objek)
        return that;
    };

Pada contoh di atas, kita membuat constructor untuk objek negara yang menerima parameter berupa objek berisi data negara yang ingin dibuat. Nilai data kemudian diisikan pda semua variabel privat yang ada, dan beberapa nilai digunakan untuk fungsi privat.

Langkah pertama dari tahap pembuatan objek di atas cukup jelas, di mana kita hanya membuat variabel dan fungsi saja. Langkah kedua juga cukup mudah, pembuatan objek kosong dengan nama that. Objek yang dikembalikan tidak wajib diberi nama that, tetapi nama ini sangat umum digunakan.

Pada langkah ketiga, kita mengisikan method publik yang akan dimiliki that. Beberapa hal yang perlu diperhatikan pada langkah ini:

  • Pembuatan method dapat dilakukan dengan dua cara. Cara pertama yaitu dengan langsung membuat fungsi baru ke variabel milik that. Jalan kedua adalah dengan mengikatkan fungsi privat ke fungsi publik. Cara pembuatan method publik kedua lebih disarankan, karena lebih aman. Misalnya, jika method that.dataLengkap disabotase, fungsi-fungsi lain di dalam objek yang memanggil method ini tidak akan terpengaruh karena sebenarnya mereka memanggil fungsi privat.
  • Nama dari method publik tidak harus sama dengan method privat, meskipun kita hanya mengikatkan method privat ke publik. Contoh jelasnya, lihat dataPenduduk dan setDataPenduduk.

Langkah keempat pastinya tidak memerlukan penjelasan lebih lanjut.

Penggunaan dari constructor negara juga sangat mudah. Cukup buat data yang diperlukan, kemudian panggil fungsi negara:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
var dataIndonesia = {
        nama: "Indonesia",
        penduduk: 400000000,
        benua: "Asia"
    };

    var indonesia = negara(dataIndonesia);

    indonesia.nama;           // mencetak undefined

    indonesia.dataNama();     // mencetak "Indonesia"
    indonesia.dataPenduduk(); // mencetak 400000000
    indonesia.dataLengkap();  // mencetak "Indonesia 400000000 Asia"

    indonesia.setDataPenduduk(410000000);
    indonesia.dataPenduduk(); // mencetak 410000000

Oke, cukup mudah dan jelas. Mari kita kembali ke topik pewarisan. Bagaimana melakukan pewarisan dengan cara fungsional? Sederhana, kita cukup mengisikan that dengan objek pewaris yang diinginkan. Dengan mengisikan that maka kita dapat menambahkan dan mengubah properti maupun method dari that. Karena objek turunan memiliki constructor tersendiri, maka kita juga dapat menambahkan nilai privat ke objek turunan.

Langsung saja, contoh jika kita ingin menurunkan objek negara:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
    var provinsi = function (data) {
        // validasi data
            // untuk memastikan data yang dikirimkan ke
            // negara tidak kosong / salah.
        data.nama = data.nama || "Nama Kosong";
        data.penduduk = data.penduduk || 0;
        data.benua = data.benua || "Benua Kosong";

        var pulau = data.pulau || "Pulau Kosong",
            that = negara(data); // that merupakan negara

        that.dataLengkap = function () {
            return data.nama + " " + pulau + " " + that.dataPenduduk();
        };

        return that;
    };

Pada objek provinsi di atas, kita mendapatkan warisan dari negara dengan sangat mudah. Dengan memanggil constructor negara maka kita mengisikan that dengan objek negara. Kita lalu mengubah method dataLengkap dengan menambahkan informasi baru ke nilai kembalian. Lihat juga bahwa kita tidak memanggil that.dataLengkap, karena pemanggilan that.dataLengkap di dalam dirinya sendiri akan menyebabkan pemanggilan fungsi secara berputar-putar tanpa akhir. Coba ganti that.dataPenduduk dengan that.dataLengkap untuk melihat efeknya.

Kita dapat mencoba menggunakan objek provinsi di atas seperti berikut:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
var dataJakarta = {
        nama: "Jakarta",
        penduduk: 1000000,
        benua: "Asia",
        pulau: "Jawa"
    };

    var jakarta = provinsi(dataJakarta);

    jakarta.dataLengkap(); // mencetak "Jakarta Jawa 1000000"

Pewarisan secara fungsional seperti ini juga memungkinkan kita memanggil method milik objek asal. Pada bahasa lain umumnya kita memanggil nilai pada objek asal dengan menggunakan kata kunci super. Mengikuti konvensi yang sudah ada, mari kita kembangkan fungsi yang memberikan kita kemampuan ini:

1
2
3
4
5
6
7
8
9
// ingat Function.prototype.method pada contoh di awal bab
Object.method('super', function (name) {
    var that = this,
        method = that[name];

    return function () {
        return method.apply(that, arguments);
    };
});

Fungsi super cukup gamblang. Kita hanya mengambil nilai objek sekarang, dan menjalankan method objek tersebut secara tidak langsung. Nilai kembalian diberikan berupa fungsi yang masih harus dieksekusi. Hal ini dibuat untuk meningkatkan fleksibilitas. Penggunaan fungsi ini adalah sebagai berikut:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
var provinsi = function (data) {
        // validasi data
            // untuk memastikan data yang dikirimkan ke
            // negara tidak kosong / salah.
        data.nama = data.nama || "Nama Kosong";
        data.penduduk = data.penduduk || 0;
        data.benua = data.benua || "Benua Kosong";

        var that = negara(data), // that merupakan negara
            pulau = data.pulau || "Pulau Kosong",
            super_dl = that.super('dataLengkap'); // pemanggilan super

        that.dataLengkap = function () {
            return pulau + " " + super_dl();
        };

        return that;
    };

    jakarta.dataLengkap(); // mencetak "Jawa Jakarta 1000000 Asia"

Sejauh ini, kita dapat melihat bagaimana pewarisan secara fungsional memberikan kita pembungkusan informasi dan akses ke method pada objek induk. Kedua hal ini, ditambahkan dengan konsep PBO yang baik akan memberikan kita objek yang sangat aman dan stabil.

Dengan pembungkusan informasi, kita dapat menyimpan semua data keadaan objek. Dengan membuat data keadaan menjadi privat, kita akan mendapatkan objek yang tahan banting. Perubahan objek hanya dapat dilakukan melalui method publik, dan secara otomatis lebih mudah diprediksi. Properti dan method dari objek dapat diubah, tetapi karena tidak dapat mengubah nilai privat, integritas objek tetap terjaga. Selama objek tidak menggunakan nilai this dan that, maka objek dapat dikatakan objek yang durable. Objek durable merupakan objek yang hanya berupa kumpulan fungsi yang berlaku sebagai capability dari objek tersebut.

Objek yang durable tidak dapat dengan mudah diserang. Jika penyerang berhasil mendapatkan kontrol pada objek durable, si penyerang tetap tidak dapat mengubah data keadaan objek tersebut.

Kombinasi Objek (Mixin)

Ketika mengembangkan aplikasi berorientasi objek terkadang kita ingin menggunakan satu atau dua fungsi saja dari sebuah objek pada objek yang sedang aktif sekarang. Menambahkan objek tersebut sebagai properti dari objek yang aktif terasa sangat mubazir, terutama jika fungsi tersebut benar-benar hanya dipanggil sekali. Hal seperti ini sering dihadapi, dan akhirnya kita harus membuat pertimbangan apakah ingin meningkatkan kompleksitas kode dengan membuat objek baru atau mengurangi kemudahan perawatan dengan mengimplementasikan dua fungsi sama pada objek yang berbeda.

Dalam Javascript, hal di atas dapat diatasi dengan teknik yang dikenal dengan nama Mixin. Konsep mixin sangat sederhana: kita “menggabungkan” dua atau lebih objek dengan hanya mengambil method atau properti baru yang diperlukan dari masing-masing objek. Bayangkan objek Javascript sebagai lego, yang dapat dipecah-pecah menjadi potongan kecil dan potongan tersebut dapat dipasangkan ke lego yang lain. Dengan cara ini kita dapat mengambil bagian yang diperlukan dari sebuah objek dan memasangkannya ke objek lain.

Implementasi dari mixin juga sagnat sederhana. Pada bagian sebelumnya kita telah melihat bagaimana kita dapat menggunakan method hasOwnProperty untuk melakukan pengecekan properti asli dari fungsi, dan bagaimana menambahkan method baru ke dalam objek dengan menggunakan prototype. Dengan menggabungkan kedua hal tersebut kita bisa mendapatkan mixin:

1
2
3
4
5
6
7
function mixin(asli, gabung) {
    for (prop in gabung) {
        if (!asli.hasOwnProperty(prop)) {
            asli[prop] = gabung[prop];
        }
    }
    }

Fungsi mixin di atas melakukan hal yang sangat sederhana. Variabel asli merupakan objek yang ingin menggunakan satu atau semua method dari objek gabung. Pertama, kita melakukan penelusuran terhadap properti milik gabung, kemudian memastikan properti tersebut tidak dimiliki oleh asli melalui fungsi hasOwnProperty. Setelah itu, kita langsung menambahkan properti baru ke dalam asli, diisikan dengan nilai yang sama dengan properti milik gabung. Properti baru ini adalah properti milik gabung yang kita telusuri.

Berikut adalah contoh penggunaan fungsi mixin di atas:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
var sapu = {
    nama: "nimbus"
}

var pesawat = {
    model: "2000",
    terbang: function () {
        return "ngoooong";
    }
};

mixin(sapu, pesawat);

console.log(sapu);
// hasil:
// {
//     nama: 'nimbus',
//     model: '2000',
//     terbang: function () {
//         return "ngoooong";
//     }
// }

Sangat mudah bukan? Sayangnya penggabungan seluruh properti objek seperti di atas tidak selalu kita inginkan. Seperti yang dijelaskan di awal, terkadang kita hanya ingin menggunakan satu properti atau method saja. Kita dapat mengganti mixin utnuk menambahkan kemampuan ini:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
function mixin(asli, gabung) {
    if (arguments.length > 2) {
        for (var i = 2, len = arguments.length; i < len; i += 1) {
            if (!asli.hasOwnProperty(arguments[i])) {
                asli[arguments[i]] = gabung[arguments[i]];
            }
        }
    } else {
        for (prop in gabung) {
            if (!asli.hasOwnProperty(prop)) {
                asli[prop] = gabung[prop];
            }
        }
    }
}

Pada kode di atas, kita menambahkan sebuah kondisi pada fungsi mixin. Ketika argumen yang diberikan ke mixin lebih dari dua, kita akan mengambil seluruh argumen, dari argumen ketiga sampai habis, dan menambahkan properti atau method dengan nama sesuai isi argumen ke asli. Nilai dari properti atau method yang ditambahkan tentunya berasal dari gabung. Mari kita lihat contoh penggunaan fungsi ini:

 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
33
34
35
36
37
38
39
var sapu = {
    nama: "nimbus"
}

var pesawat = {
    model: "2000",
    terbang: function () {
        return "ngoooong";
    }
};

mixin(sapu, pesawat, "model");

console.log(sapu);
// hasil:
// {
//     nama: 'nimbus',
//     model: '2000',
// }

var manusia = {
    kepala: 1,
    mata: 2,
    telinga: 2
}

var burung = {
    sayap: 2
}

mixin(burung, manusia, "kepala", "mata");

console.log(burung);
// hasil:
// {
//     sayap: 2,
//     kepala: 1,
//     mata: 2,
// }

Mixin merupakan salah satu teknik yang paling berguna dalam Javascript. Dengan menggunakan teknik ini, kita dapat mengurangi pengulangan fungsionalitas dalam sistem sembari meningkatkan penggunaan kembali kode. Mixin membantu kita dalam memastikan fungsionalitas yang diimplementasikan oleh seluruh komponen aplikasi kita unik dan jelas terpisah.

Begitupun, terdapat sedikit kekurangan pada mixin. Ada pendapat dari pengembang bahwa mencampurkan fungsi seperti ini dapat menyebabkan kebingungan dalam hal penelusuran asal sebuah fungsi. Semakin sulit kita mengetahui asal usul dari sebuah fungsi, tentunya semakin sulit kita merawat kode pada aplikasi. Hal ini terutama akan sangat terasa untuk aplikasi yang besar dan kompleks.

Salah satu solusi untuk masalah ini adalah dengan menulis dokumentasi secara gamblang ketika kita menggunakan mixin. Hal terpenting yang harus kita catat adalah asal usul fungsi atau properti yang akan dicampurkan, serta kenapa nilai tersebut harus digabungkan ke dalam objek asal. Jadi ingat, berhati-hatilah jika ingin menggunakan mixin. Pastikan anda mendokumentasikan penggunaannya dengan jelas, agar tidak kewalahan pada masa perawatan nantinya.

Pemanggilan Method Berantai

Salah satu hal yang paling sering kita lakukan ketika bekerja dengan objek adalah memanggil beberapa method objek yang sama secara berurutan. Mungkin kita ingin melakukan konfigurasi terhadap sebuah objek sebelum memanggil method tertentu. Atau mungkin juga kita hanya ingin mengubah data yang ada dalam objek tersebut.

Kita biasanya harus memanggil objek yang sama berkali-kali jika ingin melakukan hal di atas, seperti berikut:

1
2
3
4
var judul = App.getElement("#judul");
judul.setText('Judul Halaman');
judul.setStyle('font-weight', 'bold');
judul.setStyle('font-variant', 'small-caps');

Penulisan kode seperti ini sangat sering ditemui, sehingga library Javascript pada umumnya menyediakan fitur untuk memudahkan kita melakukan hal ini. Kemudahan biasanya ditawarkan dengan memberikan kita fitur untuk menjalankan method berkali-kali pada satu objek:

1
2
3
4
var judul = App.getElement("#judul");
judul.setText('Judul Halaman')
     .setStyle('font-weight', 'bold')
     .setStyle('font-variant', 'small-caps');

Tujuan utama dari pemanggilan seperti ini adalah untuk mengurangi penulisan nama objek. Dengan mengurangi penulisan nama objek, kemungkinan kesalahan penulisan semakin sedikit, yang tentunya akan mengurangi kesalahan program. Ingat, Javascript tidak memiliki compiler untuk mengingatkan kita bahwa variabel yang dipanggil tidak ada. Aplikasi tetap akan berjalan seperti biasa, sampai di bagian yang salah, dan tiba-tiba berhenti.

Kegunaan lain dari pemanggilan berantai seperti ini adalah kemudahan pembacaan kode. Dengan menggunakan pemanggilan berantai kita dapat langsung melihat bahwa objek sedang dikonfigurasi atau memiliki langkah eksekusi yang spesifik. Seringkali kita seolah-olah memilki bahasa khusus ketika menggunakan teknik ini. Seperti yang dapat dilihat dari contoh, pemanggilan berantai seperti ini biasanya dilakukan dengan method yang melakukan konfigurasi atau perubahan nilai objek.

Oke, kita sudah melihat kenapa teknik ini bagus. Sekarang pertanyaannya adalah, bagaimana kita dapat menggunakan teknik ini? Jawabannya sangat sederhana: kita cukup mengembalikan this pada setiap akhir method.

 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
var pesawat = {
    setSayap: function (n) {
        this.sayap = n;
        return this;
    },
    setRoda: function (n) {
        this.roda = n;
        return this;
    },
    setPenumpang: function (n) {
        this.penumpang = n;
        return this;
    },
    lepasLandas: function () {
        console.log("Pesawat berisi " + this.penumpang + " orang!");
        console.log("Pesawat memiliki " + this.sayap + " buah sayap!");
        console.log("Pesawat memiliki " + this.roda + " buah roda!");
        console.log("Pesawat lepas landas!");
    }
};

// Kode di bawah mencetak:
// Pesawat berisi 200 orang!
    // Pesawat memiliki 2 buah sayap!
    // Pesawat memiliki 3 buah roda!
    // Pesawat lepas landas!
pesawat.setSayap(2)
       .setRoda(3)
       .setPenumpang(200)
       .lepasLandas();

Dari kode di atas, kita dapat melihat bagaimana pada semua method yang mengembalikan this kita dapat langsung memanggil method selanjutnya secara berantai. Kita tidak dapat langsung menggunakan teknik ini pada objek yang tidak mengimplementasikan pengembalian this, sehingga pemanggilan berantai tidak dapat dilakukan pada console. Kita juga tidak dapat melakuakn pemanggilan method tambahan setelah memanggil method yang tidak mengembalikan this. Sebagai bahan latihan, coba jalankan kode di bawah setelah mengeksekusi kode sebelumnya, dan lihat hasilnya:

1
2
3
pesawat.setSayap(2)
       .lepasLandas()
       .setRoda(4);

Hasil eksekusi dari kode di atas adalah TypeError: Cannot call method 'setRoda' of undefined. Alasan kenapa kita mendapatkan pesan kesalahan ini akan dilewatkan, dan dianggap sebagai latihan bagi pembaca.

comments powered by Disqus
Kembali ke bertzzie.com