Lihatlah potongan kode berikut yang memecahkan masalah yang sama, dan pikirkan yang mana yang paling Anda sukai.
Ini yang pertama: | Ini yang kedua: |
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .filter(int => isEven(int)) .filter(int => isBiggerThan(3, int)) .map(int => int + 1) .map(int => toChar(int)) .filter(char => !isVowel(char)) .join('') // 'fhjl'
| [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .filter(isEven) .filter(isBiggerThan(3)) .map(plus(1)) .map(toChar) .filter(not(isVowel)) .join('') |
”Saya bertaruh bahwa opsi kedua adalah keterbacaan yang jauh lebih baik daripada yang pertama,” kata penulis materi, terjemahan yang kami terbitkan hari ini. Menurutnya, seluruh poinnya ada pada argumen metode
filter()
dan
map()
.

Hari ini kita akan berbicara tentang cara mendaur ulang kode yang mirip dengan contoh pertama sehingga terlihat seperti kode dari yang kedua. Penulis artikel berjanji bahwa setelah Anda memahami cara kerjanya, Anda akan berhubungan dengan program Anda dengan cara yang baru dan Anda tidak akan dapat mengabaikan apa yang sebelumnya tampak normal dan tidak memerlukan perbaikan.
Fungsi sederhana
Pertimbangkan fungsi
sum()
sederhana yang menambahkan angka yang diteruskan ke dalamnya:
const sum = (a, b) => a + b sum(1, 2)
Kami menulis ulang, memberikan fungsi baru nama
csum()
:
const csum = a => b => a + b csum(1)(2)
Versi barunya bekerja dengan cara yang persis sama dengan versi aslinya, satu-satunya perbedaan adalah bagaimana fungsi baru ini disebut. Yaitu, fungsi
sum()
mengambil dua parameter sekaligus, dan
csum()
mengambil parameter yang sama satu per satu. Bahkan, saat memanggil
csum()
, dua fungsi dipanggil. Secara khusus, pertimbangkan situasi ketika
csum()
dipanggil,
csum()
nomor 1 dan tidak ada yang lain:
csum(1)
Panggilan ke
csum()
mengarah pada fakta bahwa ia mengembalikan fungsi yang dapat menerima argumen numerik kedua yang diteruskan ke
csum()
selama panggilan biasa, dan mengembalikan hasil menambahkan satu ke argumen ini. Panggil fungsi ini
plusOne()
:
const plusOne = csum(1) plusOne(2)
Bekerja dengan array
Dalam JavaScript, Anda dapat bekerja dengan array menggunakan berbagai metode khusus. Katakanlah metode
map()
digunakan untuk menerapkan fungsi yang diteruskan ke setiap elemen array.
Misalnya, untuk menambah 1 elemen setiap array integer (lebih tepatnya, untuk membentuk array baru yang mengandung elemen array asli meningkat sebesar 1), Anda dapat menggunakan konstruksi berikut:
[1, 2, 3].map(x => x + 1)
Dengan kata lain, apa yang terjadi dapat digambarkan sebagai berikut: fungsi
x => x + 1
mengambil bilangan bulat dan mengembalikan angka yang mengikutinya dalam serangkaian bilangan bulat. Menggunakan fungsi
plusOne()
dibahas di atas, contoh ini dapat ditulis ulang sebagai berikut:
[1, 2, 3].map(x => plusOne(x))
Di sini perlu untuk memperlambat dan berpikir tentang apa yang terjadi. Jika Anda melakukan ini, Anda dapat melihat bahwa dalam kasus yang dipertimbangkan, konstruksi
x => plusOne(x)
dan
plusOne
(perhatikan bahwa dalam situasi ini tidak ada tanda kurung setelah nama fungsi) adalah setara. Untuk lebih memahami hal ini, pertimbangkan fungsi
otherPlusOne()
:
const otherPlusOne = x => plusOne(x) otherPlusOne(1)
Hasil dari fungsi ini akan sama dengan yang diperoleh dengan panggilan sederhana ke
plusOne()
sudah kita kenal:
plusOne(1)
Untuk alasan yang sama, kita dapat berbicara tentang kesetaraan dua konstruksi berikut. Inilah yang pertama yang telah kita lihat:
[1, 2, 3].map(x => plusOne(x))
Ini yang kedua:
[1, 2, 3].map(plusOne)
Selain itu, ingatlah bagaimana fungsi
plusOne()
dibuat:
const plusOne = csum(1)
Ini memungkinkan kami untuk menulis ulang konstruksi kami dengan
map()
sebagai berikut:
[1, 2, 3].map(csum(1))
Sekarang, dengan menggunakan teknik yang sama,
isBiggerThan()
. Jika Anda mau, coba lakukan sendiri, lalu baca terus. Ini akan menghilangkan penggunaan konstruksi yang tidak perlu saat menggunakan metode
filter()
. Pertama, mari bawa kode ke formulir ini:
const isBiggerThan = (threshold, int) => int > threshold [1, 2, 3, 4].filter(int => isBiggerThan(3, int))
Kemudian, menyingkirkan semua yang tidak perlu, kami mendapatkan kode yang sudah Anda lihat di awal materi ini:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .filter(isEven) .filter(isBiggerThan(3)) .map(plus(1)) .map(toChar) .filter(not(isVowel)) .join('')
Kami sekarang mempertimbangkan dua aturan sederhana yang memungkinkan Anda untuk menulis kode dengan gaya yang dibahas di sini.
Peraturan nomor 1
Dua konstruksi berikut ini setara:
[…].map(x => fnc(x)) […].map(fnc)
Peraturan nomor 2
Callback selalu dapat ditulis ulang untuk mengurangi jumlah argumen yang digunakan untuk menyebutnya:
const fnc = (x, y, z) => … […].map(x => fnc(x, y, z)) const fnc = (y, z) => x => … […].map(fnc(y, z))
Jika Anda sendiri yang menulis fungsi
isBiggerThan()
, maka Anda mungkin sudah beralih ke transformasi seperti itu. Misalkan kita membutuhkan angka yang lebih besar dari 3 untuk melewati filter. Ini dapat dilakukan seperti ini:
const isBiggerThan = (threshold, int) => int > threshold […].filter(int => isBiggerThan(3, int))
Sekarang kita menulis ulang fungsi
isBiggerThan()
sehingga dapat digunakan dalam metode
filter()
dan tidak menggunakan
int=>
construct:
const isBiggerThan = threshold => int => int > threshold […].map(isBiggerThan(3))
Latihan
Misalkan kita memiliki fragmen kode berikut:
const keepGreatestChar = (char1, char2) => char1 > char2 ? char1 : char2 keepGreatestChar('b', 'f') // 'f' // 'f' 'b'
Sekarang, berdasarkan fungsi
keepGreatestChar()
, buat fungsi
keepGreatestCharBetweenBAnd()
. Kita memerlukan itu, dengan memanggilnya, kita hanya bisa menyampaikan satu argumen, sementara itu akan membandingkan karakter yang diteruskan dengan karakter
b
. Fungsi ini mungkin terlihat seperti ini:
const keepGreatestChar = (char1, char2) => char1 > char2 ? char1 : char2 const keepGreatestCharBetweenBAnd = char => keepGreatestChar('b', char) keepGreatestCharBetweenBAnd('a')
Sekarang tuliskan fungsi
greatestCharInArray()
, yang, menggunakan fungsi
keepGreatestChar()
dalam metode array
keepGreatestChar()
, memungkinkan Anda untuk mencari karakter "terbesar" dan tidak memerlukan argumen. Mari kita mulai dengan kode ini:
const keepGreatestChar = (char1, char2) => char1 > char2 ? char1 : char2 const greatestCharInArray = array => array.reduce((acc, char) => acc > char ? acc : char, 'a') greatestCharInArray(['a', 'b', 'c', 'd'])
Untuk mengatasi masalah ini, terapkan fungsi
creduce()
, yang dapat digunakan dalam fungsi
greatestCharInArray()
, yang memungkinkan, dalam aplikasi praktis fungsi ini, untuk tidak meneruskan apa pun padanya kecuali array untuk menemukan karakter dengan kode terbesar.
Fungsi
creduce()
harus cukup universal sehingga dapat digunakan untuk menyelesaikan masalah yang membutuhkan penggunaan kemampuan dari metode array
creduce()
standar. Dengan kata lain, fungsi harus menerima panggilan balik, nilai awal, dan array untuk bekerja dengannya. Akibatnya, Anda harus mendapatkan fungsi yang dapat digunakan fragmen kode berikut:
const greatestCharInArray = creduce(keepGreatestChar, 'a') greatestCharInArray(['a', 'b', 'c', 'd'])
Ringkasan
Mungkin sekarang Anda memiliki pertanyaan tentang mengapa metode, diproses sesuai dengan metodologi yang disajikan di sini, memiliki nama yang dimulai dengan karakter
c
. Karakter
c
adalah akronim untuk kari, dan kami berbicara tentang bagaimana fungsi kari membantu meningkatkan keterbacaan kode. Perlu dicatat bahwa di sini kami tidak berusaha keras untuk mematuhi prinsip-prinsip pemrograman fungsional, tetapi, kami percaya bahwa aplikasi praktis dari apa yang dibahas di sini memungkinkan kami untuk meningkatkan kode. Jika topik currying dalam JavaScript menarik untuk Anda - disarankan untuk membaca bab 4 buku
ini tentang pemrograman fungsional, dan, secara umum, karena Anda telah mencapai titik ini - baca seluruh buku ini. Juga, jika Anda baru mengenal pemrograman fungsional, lihat materi pemula
ini .
Pembaca yang budiman! Apakah Anda menggunakan currying fungsi dalam pengembangan JavaScript?
