Cara cepat memulai VUE3.0: Masuk ke
Rekomendasi Terkait Pembelajaran: Tutorial Pembelajaran JavaScript
Saya telah melihat banyak penjelasan tentang pemrograman fungsional, namun sebagian besar berada pada tataran teoretis, dan ada pula yang hanya untuk bahasa pemrograman fungsional murni seperti Haskell. Tujuan artikel ini adalah untuk berbicara tentang praktik spesifik pemrograman fungsional dalam JavaScript di mata saya. Alasan mengapa ini "di mata saya" berarti bahwa apa yang saya katakan hanya mewakili pendapat pribadi saya, yang mungkin bertentangan dengan beberapa konsep ketat.
Artikel ini akan menghilangkan banyak pengenalan konsep formal, dan fokus pada menunjukkan apa itu kode fungsional dalam JavaScript, apa perbedaan antara kode fungsional dan penulisan umum, apa manfaat kode fungsional bagi kita, dan apa saja model fungsionalnya?
Menurut saya pemrograman fungsional dapat dipahami sebagai metode pemrograman yang menggunakan fungsi sebagai pembawa utama. Menggunakan fungsi untuk membongkar dan mengabstraksi ekspresi umum
dibandingkan dengan imperatif. Poin utamanya adalah sebagai berikut:
semantik yang lebih jelas, penggunaan kembali yang lebih tinggi, pemeliharaan yang lebih tinggi, batasan cakupan yang lebih baik, dan efek samping yang lebih sedikit. Contoh berikut adalah ekspresi fungsional spesifik dari
kode Javascript
// setiap kata dalam array, Gunakan huruf kapital huruf pertama // Penulisan umum const arr = ['apple', 'pen', 'apple-pen']; konstanta c = arr[i][0]; arr[i] = c.toUpperCase() + arr[i].slice(1 } konsol.log(arr); // Metode penulisan fungsional - function upperFirst(kata) { kembalikan kata[0].toUpperCase() + kata.slice(1 } fungsi wordToUpperCase(arr) { return arr.map(atasPertama); console.log(wordToUpperCase(['apple', 'pen', 'apple-pen'])); // Metode penulisan fungsional 2 console.log(arr.map(['apple', 'pen', 'apple-pen'], word => word[0].toUpperCase() + word.slice(1))) ;
Ketika situasinya menjadi lebih kompleks, cara menulis ekspresi akan menghadapi beberapa masalah:
maknanya tidak jelas, secara bertahap menjadi sulit untuk dipertahankan, penggunaan kembali buruk, lebih banyak kode akan dihasilkan, dan banyak variabel perantara akan dihasilkan. Pemrograman fungsional dapat menyelesaikan masalah di atas dengan baik. Pertama, lihat metode penulisan fungsional 1, yang menggunakan enkapsulasi fungsi untuk menguraikan fungsi (perinciannya tidak unik), merangkumnya ke dalam fungsi yang berbeda, dan kemudian menggunakan panggilan gabungan untuk mencapai tujuan. Hal ini membuat ekspresi menjadi jelas dan mudah dipelihara, digunakan kembali, dan diperluas. Kedua, dengan menggunakan fungsi tingkat tinggi, Array.map menggantikan for...of untuk traversal array, sehingga mengurangi variabel dan operasi perantara.
Perbedaan utama antara metode penulisan fungsional 1 dan metode penulisan fungsional 2 adalah Anda dapat mempertimbangkan apakah fungsi tersebut dapat digunakan kembali nanti. Jika tidak, yang terakhir lebih baik.
Dari metode penulisan fungsional 2 di atas, kita dapat melihat bahwa dalam proses penulisan kode fungsional, mudah untuk menyebabkan perluasan horizontal, yaitu menghasilkan banyak lapisan yang bersarang. Mari kita berikan contoh ekstrim di bawah ini.
Kode Javascript
// Menghitung jumlah angka // Metode penulisan umum console.log(1 + 2 + 3 - 4) // Penulisan fungsional function sum(a, b) { kembalikan a + b; } fungsi sub(a, b) { kembalikan a - b; console.log(sub(sum(sum(1, 2), 3), 4); Contoh ini hanya menunjukkan kasus ekstrim ekstensi horizontal. Karena jumlah level fungsi yang disarangkan terus meningkat, kode akan menjadi kurang mudah dibaca . Performanya sangat berkurang dan mudah membuat kesalahan. Dalam hal ini, kita dapat mempertimbangkan beberapa metode pengoptimalan, seperti pengoptimalan rantai berikut. // Optimalkan penulisan (ya, Anda membacanya dengan benar, ini adalah penulisan berantai lodash) Kode Javascript const utils = { rantai(a) { ini._temp = a; kembalikan ini; }, jumlah(b) { ini._temp += b; kembalikan ini; }, sub(b) { ini._temp -= b; kembalikan ini; }, nilai() { const _temp = ini._temp; this._temp = tidak terdefinisi; kembalikan _temp; } }; console.log(utils.chain(1).sum(2).sum(3).sub(4).value());
Setelah menulis ulang dengan cara ini, keseluruhan struktur akan menjadi lebih jelas, dan setiap tautan dalam rantai akan Apa yang harus dilakukan juga dapat ditampilkan dengan mudah. Contoh bagus lainnya dari perbandingan antara fungsi nesting dan chaining adalah fungsi callback dan pola Promise.
Kode Javascript
// Minta dua antarmuka secara berurutan // Fungsi panggilan balik import $ dari 'jquery'; jika(rs){ $.post('a/url/ke/yang lain/target', (rs2) => { jika(rs2){ $.post('a/url/ke/ketiga/target'); } }); } }); // Permintaan impor janji dari 'catta'; // catta adalah alat permintaan ringan yang mendukung pengambilan, jsonp, ajax, dan tidak memiliki dependensi request('a/url/to/target') .then(rs => rs ? $.post('a/url/ke/another/target') : Janji.tolak()) .then(rs2 => rs2 ? $.post('a/url/to/third/target') : Promise.reject());
Saat fungsi panggilan balik tingkat bersarang dan kompleksitas tingkat tunggal meningkat, itu akan menjadi Itu adalah membengkak dan sulit dipertahankan, namun struktur rantai Promise masih dapat berkembang secara vertikal ketika kompleksitasnya tinggi, dan isolasi hierarki sangat jelas.
Penutupanmodel pemrograman fungsional yang umum
dapat mempertahankan variabel lokal dalam blok kode yang tidak dirilis. Ini disebut penutupan.
Konsep penutupan relatif abstrak. Saya yakin semua orang tahu lebih banyak atau lebih sedikit dan menggunakan fitur
ini bisakah tas membawa kita?
Pertama mari kita lihat cara membuat penutupan:
Kode Javascript
// Membuat fungsi penutupan makeCounter() { misalkan k = 0; fungsi kembali() { kembalikan ++k; }; const penghitung = makeCounter(); console.log(counter()); // 1 console.log(counter()); // 2
makeCounter Blok kode fungsi ini mereferensikan variabel lokal k dalam fungsi yang dikembalikan, menyebabkan variabel lokal gagal fungsi dijalankan, maka didaur ulang oleh sistem, sehingga menghasilkan penutupan. Fungsi penutupan ini adalah untuk "mempertahankan" variabel lokal sehingga variabel tersebut dapat digunakan kembali ketika fungsi dalam dipanggil; tidak seperti variabel global, variabel ini hanya dapat direferensikan di dalam fungsi.
Dengan kata lain, penutupan sebenarnya menciptakan beberapa "variabel persisten" yang bersifat pribadi bagi fungsi tersebut.
Jadi dari contoh ini kita dapat menyimpulkan bahwa syarat untuk membuat penutupan adalah:
ada fungsi dalam dan luar. Fungsi dalam mengacu pada variabel lokal dari fungsi luar beberapa fungsi. Variabel persisten dengan domain terbatas, variabel ini dapat digunakan untuk caching atau penghitungan perantara, dll.
Kode Javascript
// Alat caching sederhana // Fungsi anonim membuat penutupan const cache = (function() { toko const = {}; kembali { dapatkan(kunci) { kembalikan toko[kunci]; }, set(kunci, val) { toko[kunci] = val; } } }()); cache.set('a', 1); cache.get('a'); // 1Contoh
di atas adalah implementasi alat caching sederhana yang membuat penutupan sehingga objek penyimpanan selalu dapat direferensikan. , tidak akan didaur ulang.
Kerugian dari penutupan: variabel persisten tidak akan dirilis secara normal dan terus menempati ruang memori, yang dapat dengan mudah menyebabkan pemborosan memori, sehingga biasanya diperlukan beberapa mekanisme pembersihan manual tambahan.
Fungsi yang menerima atau mengembalikan suatu fungsi disebut fungsi tingkat tinggi.
Kedengarannya seperti kata yang sangat dingin, namun sebenarnya kita sering menggunakannya, tetapi kita tidak mengetahui namanya. Bahasa JavaScript secara asli mendukung fungsi tingkat tinggi, karena fungsi JavaScript adalah warga kelas satu, dan dapat digunakan baik sebagai parameter maupun sebagai nilai kembalian dari fungsi lain.
Kita sering melihat banyak fungsi tingkat tinggi asli dalam JavaScript, seperti Array.map, Array.reduce, dan Array.filter.
Mari kita lihat bagaimana dia menggunakan
peta (pemetaan
Dengan kata lain, setiap item dalam himpunan mengalami transformasi yang sama untuk menghasilkan
peta himpunan baru. Sebagai fungsi tingkat tinggi, ia menerima parameter fungsi sebagai
kode Javascript
logis dari pemetaan// Tambahkan satu ke setiap item dalam array untuk membentuk array baru // Metode penulisan umum const arr = [1,2,3]; const rs = []; for(const n of arr){ rs.push(++n); } konsol.log(rs) // map menulis ulang const arr = [1,2,3]; const rs = arr.map(n => ++n);
metode penulisan umum di atas, menggunakan for...of loop untuk melintasi array akan menyebabkan operasi tambahan, dan Ada risiko mengubah array asli
, tetapi fungsi map merangkum operasi yang diperlukan, sehingga kita hanya perlu memperhatikan implementasi fungsi logika pemetaan, yang mengurangi jumlah kode dan risiko sampingan. efek.
memberikan beberapa parameter suatu fungsi dan menghasilkan fungsi baru yang menerima parameter lain.
Anda mungkin tidak sering mendengar istilah ini, tetapi siapa pun yang pernah menggunakan undescore atau lodash pasti pernah melihatnya.
Ada fungsi ajaib _.partial, yang merupakan kari
kode Javascript
// Dapatkan jalur relatif dari file target ke jalur dasar // Metode penulisan umum adalah const BASE = '/path/to/base'; jalur.relatif(BASE, '/some/path'); // _.parical menulis ulang const BASE = '/path/to/base'; const relativeFromBase = _.partial(path.relative, BASE); const relativePath = relativeFromBase('/some/path');
Melalui _.partial, kita mendapatkan fungsi baru relativeFromBase. Saat fungsi ini dipanggil, ini setara dengan memanggil path.relative, dan parameter pertama diteruskan ke BASE secara default . Parameter selanjutnya yang diteruskan ditambahkan secara berurutan.
Dalam hal ini, apa yang sebenarnya ingin kita capai adalah mendapatkan jalur yang relatif terhadap BASE setiap saat, bukan relatif terhadap jalur mana pun. Currying memungkinkan kita untuk hanya memperhatikan beberapa parameter suatu fungsi, menjadikan tujuan fungsi lebih jelas dan memanggilnya lebih sederhana.
menggabungkan kemampuan beberapa fungsi untuk membuat fungsi baru.
Anda mungkin pernah melihatnya pertama kali di lodash, metode penulisan (sekarang disebut aliran)
Kode Javascript
// Gunakan huruf besar untuk setiap kata dalam array, lakukan Base64 //Metode penulisan umum (salah satunya) const arr = ['pen', 'apple', 'applypen']; const rs = []; for(const w of arr){ rs.push(btoa(w.toUpperCase())); } konsol.log(rs); // _.flow menulis ulang const arr = ['pena', 'apel', 'applypen']; const upperAndBase64 = _.partialRight(_.map, _.flow(_.upperCase, btoa)); console.log(upperAndBase64(arr));
_.flow menggabungkan kemampuan fungsi konversi huruf besar dan konversi Base64 untuk menghasilkan fungsi baru. Nyaman untuk digunakan sebagai fungsi parameter atau penggunaan kembali berikutnya.
Dari sudut pandang saya sendiri, pemahaman saya tentang pemrograman fungsional JavaScript mungkin berbeda dari banyak konsep tradisional. Saya tidak hanya berpikir bahwa fungsi tingkat tinggi dihitung sebagai pemrograman fungsional. Lainnya, seperti panggilan gabungan fungsi biasa, struktur rantai, dll., Saya pikir termasuk dalam kategori pemrograman fungsional, selama mereka menggunakan fungsi sebagai fungsi utama. pembawa.
Dan menurut saya pemrograman fungsional tidak diperlukan, juga tidak boleh menjadi aturan atau persyaratan wajib. Seperti halnya berorientasi objek atau ide lainnya, ini juga salah satu caranya. Dalam kebanyakan kasus, kita harus merupakan kombinasi dari beberapa, bukan terbatas pada konsep.
Rekomendasi terkait: Tutorial JavaScript
Di atas adalah pembahasan rinci tentang pemrograman fungsional JavaScript. Untuk informasi lebih lanjut, harap perhatikan artikel terkait lainnya di situs web PHP Cina!