pengantar
Sepertinya library Lodash dan Underscore sekarang ada di mana - mana dan masih memiliki metode penulisan super efisien yang kita kenal sekarang .
Mari kita lihat lebih dekat fungsi compose dan lihat bagaimana itu bisa membuat kode Anda lebih mudah dibaca, dipelihara, dan elegan.
Dasar
Kita akan membahas banyak fungsi Lodash karena 1) kita tidak akan menulis algoritma dasar kita sendiri - ini akan mengalihkan kita dari apa yang saya sarankan untuk difokuskan; dan 2) Perpustakaan Lodash digunakan oleh banyak pengembang dan dapat diganti dengan Underscore, perpustakaan lain atau algoritma Anda sendiri tanpa masalah.
Sebelum melihat beberapa contoh sederhana, mari kita lihat apa sebenarnya fungsi compose dan bagaimana mengimplementasikan fungsi compose Anda sendiri jika perlu.
var compose = function(f, g) {
return function(x) {
return f(g(x));
};
};
Ini adalah implementasi paling dasar. Perhatikan bahwa fungsi dalam argumen akan dijalankan dari kanan ke kiri . Artinya, fungsi di sebelah kanan dijalankan lebih dulu, dan hasilnya diteruskan ke fungsi di sebelah kirinya.
Sekarang mari kita lihat kode ini:
function reverseAndUpper(str) {
var reversed = reverse(str);
return upperCase(reversed);
}
Fungsi reverseAndUpper pertama-tama membalikkan string yang diberikan dan kemudian mengubahnya menjadi huruf besar. Kita dapat menulis ulang kode ini menggunakan fungsi penulisan dasar:
var reverseAndUpper = compose(upperCase, reverse);
Fungsi reverseAndUpper sekarang dapat digunakan:
reverseAndUpper(''); //
Kita bisa mendapatkan hasil yang sama dengan menulis kode:
function reverseAndUpper(str) {
return upperCase(reverse(str));
}
Opsi ini terlihat lebih elegan dan lebih mudah dirawat serta digunakan kembali.
Kemampuan untuk mendesain fungsi dengan cepat dan membuat pipeline pemrosesan data akan berguna dalam berbagai situasi saat Anda perlu mengubah data saat dalam perjalanan. Sekarang bayangkan Anda perlu meneruskan collection ke suatu fungsi, mengubah elemen collection dan mengembalikan nilai maksimum setelah semua langkah pipeline diselesaikan, atau mengonversi string yang diberikan menjadi nilai Boolean. Fungsi compose memungkinkan Anda menggabungkan beberapa fungsi dan dengan demikian membuat fungsi yang lebih kompleks.
Mari terapkan fungsi tulis yang lebih fleksibel yang dapat menyertakan sejumlah fungsi dan argumen lain. Fungsi compose sebelumnya yang kita lihat hanya berfungsi dengan dua fungsi dan hanya menerima argumen pertama. Kita bisa menulis ulang seperti ini:
var compose = function() {
var funcs = Array.prototype.slice.call();
return funcs.reduce(function(f,g) {
return function() {
return f(g.apply(this, ));
};
});
};
Dengan fungsi seperti ini, kita bisa menulis kode seperti ini:
Var doSometing = compose(upperCase, reverse, doSomethingInitial);
doSomething('foo', 'bar');
Ada banyak pustaka yang mengimplementasikan fungsi penulisan. Kami membutuhkan fungsi compose kami untuk memahami cara kerjanya. Tentu saja, ini diimplementasikan secara berbeda di perpustakaan yang berbeda. Fungsi penulisan dari pustaka yang berbeda melakukan hal yang sama, sehingga dapat dipertukarkan. Sekarang setelah kita memahami tentang fungsi compose, mari gunakan contoh berikut untuk melihat fungsi _.compose dari library Lodash.
Contoh dari
Mari kita mulai dengan sederhana:
function notEmpty(str) {
return ! _.isEmpty(str);
}
Fungsi notEmpty adalah negasi dari nilai yang dikembalikan oleh fungsi _.isEmpty.
Kita bisa mencapai hasil yang sama menggunakan fungsi _.compose dari pustaka Lodash. Mari kita tulis fungsinya bukan:
function not(x) { return !x; }
var notEmpty = _.compose(not, _.isEmpty);
Sekarang Anda dapat menggunakan fungsi notEmpty dengan argumen apa pun:
notEmpty('foo'); // true
notEmpty(''); // false
notEmpty(); // false
notEmpty(null); // false
Ini adalah contoh yang sangat sederhana. Mari kita lihat sesuatu
yang sedikit lebih rumit: fungsi findMaxForCollection mengembalikan nilai maksimum dari kumpulan objek dengan properti id dan val (value).
function findMaxForCollection(data) {
var items = _.pluck(data, 'val');
return Math.max.apply(null, items);
}
var data = [{id: 1, val: 5}, {id: 2, val: 6}, {id: 3, val: 2}];
findMaxForCollection(data);
Fungsi compose dapat digunakan untuk menyelesaikan masalah ini:
var findMaxForCollection = _.compose(function(xs) { return Math.max.apply(null, xs); }, _.pluck);
var data = [{id: 1, val: 5}, {id: 2, val: 6}, {id: 3, val: 2}];
findMaxForCollection(data, 'val'); // 6
Ada pekerjaan yang harus diselesaikan di sini.
_.pluck mengharapkan koleksi sebagai argumen pertama dan fungsi panggilan balik sebagai yang kedua. Apakah mungkin untuk menerapkan sebagian fungsi _.pluck? Dalam hal ini, Anda dapat menggunakan kari untuk mengubah urutan argumen.
function pluck(key) {
return function(collection) {
return _.pluck(collection, key);
}
}
Fungsi findMaxForCollection perlu diubah sedikit lagi. Mari buat fungsi maks kita sendiri.
function max(xs) {
return Math.max.apply(null, xs);
}
Sekarang kita bisa membuat fungsi compose kita lebih elegan:
var findMaxForCollection = _.compose(max, pluck('val'));
findMaxForCollection(data);
Kami menulis fungsi petik kami sendiri dan hanya dapat menggunakannya dengan properti 'val'. Anda mungkin tidak mengerti mengapa Anda harus menulis metode pengambilan Anda sendiri jika Lodash sudah memiliki fungsi yang siap pakai dan nyaman
_.pluck. Masalahnya adalah ia _.pluckmengharapkan kumpulan sebagai argumen pertama, dan kami ingin melakukannya secara berbeda. Dengan mengubah urutan argumen, kita dapat menerapkan sebagian fungsi dengan melewatkan kunci sebagai argumen pertama; fungsi yang dikembalikan akan menerima data.
Kami dapat sedikit menyempurnakan metode pengambilan sampel kami. Lodash memiliki metode praktis
_.curryyang memungkinkan Anda menulis fungsi kami seperti ini:
function plucked(key, collection) {
return _.pluck(collection, key);
}
var pluck = _.curry(plucked);
Kami hanya membungkus fungsi petik asli untuk menukar argumen. Sekarang petik akan mengembalikan fungsi tersebut sampai semua argumen diproses. Mari kita lihat seperti apa kode terakhirnya:
function max(xs) {
return Math.max.apply(null, xs);
}
function plucked(key, collection) {
return _.pluck(collection, key);
}
var pluck = _.curry(plucked);
var findMaxForCollection = _.compose(max, pluck('val'));
var data = [{id: 1, val: 5}, {id: 2, val: 6}, {id: 3, val: 2}];
findMaxForCollection(data); // 6
Fungsi findMaxForCollection bisa dibaca dari kanan ke kiri, yaitu, pertama properti val dari setiap item dalam koleksi diteruskan ke fungsi, lalu mengembalikan maksimum dari semua nilai yang diterima.
var findMaxForCollection = _.compose(max, pluck('val'));
Kode ini lebih mudah dipelihara, dapat digunakan kembali, dan jauh lebih elegan. Mari kita lihat contoh terakhir, hanya untuk menikmati sekali lagi keanggunan komposisi fungsi.
Mari kita ambil data dari contoh sebelumnya dan tambahkan properti baru bernama active. Sekarang datanya terlihat seperti ini:
var data = [{id: 1, val: 5, active: true},
{id: 2, val: 6, active: false },
{id: 3, val: 2, active: true }];
Mari kita panggil fungsi ini getMaxIdForActiveItems (data) . Ini mengambil kumpulan objek, memfilter semua objek aktif, dan mengembalikan nilai maksimum yang difilter.
function getMaxIdForActiveItems(data) {
var filtered = _.filter(data, function(item) {
return item.active === true;
});
var items = _.pluck(filtered, 'val');
return Math.max.apply(null, items);
}
Bisakah Anda membuat kode ini lebih elegan? Ini sudah memiliki fungsi maks dan petik, jadi kita hanya perlu menambahkan filter:
var getMaxIdForActiveItems = _.compose(max, pluck('val'), _.filter);
getMaxIdForActiveItems(data, function(item) {return item.active === true; }); // 5
_.filterMasalah yang sama muncul
dengan fungsi sebagai dengan _.pluck: kita tidak dapat menerapkan sebagian fungsi ini, karena mengharapkan koleksi sebagai argumen pertamanya. Kita dapat mengubah urutan argumen di filter dengan membungkus fungsi awal:
function filter(fn) {
return function(arr) {
return arr.filter(fn);
};
}
Mari tambahkan fungsi
isActiveyang mengambil objek dan memeriksa apakah bendera aktif disetel ke true.
function isActive(item) {
return item.active === true;
}
Fungsi
filterdengan fungsi isActivedapat diterapkan sebagian, jadi kita hanya akan meneruskan data ke fungsi getMaxIdForActiveItems .
var getMaxIdForActiveItems = _.compose(max, pluck('val'), filter(isActive));
Sekarang kita hanya perlu mentransfer data:
getMaxIdForActiveItems(data); // 5
Sekarang tidak ada yang menghalangi kita untuk menulis ulang fungsi ini sehingga ia menemukan nilai maksimum di antara objek yang tidak aktif dan mengembalikannya:
var isNotActive = _.compose(not, isActive);
var getMaxIdForNonActiveItems = _.compose(max, pluck('val'), filter(isNotActive));
Kesimpulan
Seperti yang mungkin telah Anda perhatikan, komposisi fungsi adalah fitur menarik yang membuat kode Anda elegan dan dapat digunakan kembali. Yang utama adalah menerapkannya dengan benar.
Tautan
lodash
Hey Underscore, You're Doing It Wrong! (Hai, Garis Bawah, Anda melakukan semuanya dengan salah!)
@sharifsbeat
Baca lebih banyak: