Haruskah saya menempatkan fungsi yang hanya digunakan dalam satu fungsi lain, di dalam fungsi itu?

30

Secara khusus, saya menulis dalam JavaScript.

Katakanlah fungsi utama saya adalah Fungsi A. Jika Fungsi A membuat beberapa panggilan ke Fungsi B, tetapi Fungsi B tidak digunakan di tempat lain, maka haruskah saya menempatkan Fungsi B di dalam Fungsi A?

Apakah itu praktik yang baik? Atau haruskah saya masih menempatkan Fungsi B pada lingkup yang sama dengan Fungsi A?

Gary
sumber

Jawaban:

32

Saya umumnya mendukung fungsi bersarang, terutama dalam JavaScript.

  • Dalam JavaScript, satu-satunya cara untuk membatasi visibilitas fungsi adalah dengan bersarang di dalam fungsi lain.
  • Fungsi pembantu adalah detail implementasi pribadi. Menempatkannya pada ruang lingkup yang sama mirip dengan membuat fungsi pribadi kelas menjadi publik.
  • Jika ternyata penggunaannya lebih umum, mudah untuk pindah, karena Anda dapat yakin bahwa helper saat ini hanya digunakan oleh satu fungsi itu. Dengan kata lain, itu membuat kode Anda lebih kohesif.
  • Anda sering dapat menghilangkan parameter, yang membuat fungsi tanda tangan dan implementasi kurang bertele-tele. Ini bukan hal yang sama dengan membuat variabel global. Ini lebih seperti menggunakan anggota kelas dalam metode pribadi.
  • Anda dapat memberikan fungsi helper lebih baik, nama yang lebih sederhana, tanpa khawatir tentang konflik.
  • Fungsi pembantu lebih mudah ditemukan. Ya, ada alat yang dapat membantu Anda. Masih lebih mudah untuk hanya menggerakkan mata sedikit ke atas layar.
  • Kode secara alami membentuk semacam pohon abstraksi: beberapa fungsi umum di root, bercabang menjadi beberapa detail implementasi. Jika Anda menggunakan editor dengan fungsi lipat / runtuh, bersarang menciptakan hierarki fungsi terkait erat pada tingkat abstraksi yang sama. Itu membuatnya sangat mudah untuk mempelajari kode pada level yang Anda butuhkan dan menyembunyikan detailnya.

Saya pikir banyak oposisi datang dari kenyataan bahwa sebagian besar programmer dibesarkan dalam tradisi C / C ++ / Java, atau diajarkan oleh orang lain yang. Fungsi bersarang tidak terlihat alami karena kami tidak terlalu terpapar ketika kami sedang mempelajari program. Itu tidak berarti mereka tidak berguna.

Karl Bielefeldt
sumber
14

Anda harus meletakkannya di ruang lingkup global, karena beberapa alasan.

  • Menempatkan fungsi pembantu ke pemanggil menambah panjang pemanggil. Panjang fungsi hampir selalu merupakan indikator negatif; fungsi pendek lebih mudah dimengerti, menghafal, men-debug, dan memelihara.

  • Jika fungsi helper memiliki nama yang masuk akal, membaca nama itu sudah cukup tanpa perlu melihat definisi di dekatnya. Jika Anda lakukan perlu melihat definisi pembantu untuk memahami fungsi pemanggil, maka pemanggil melakukan terlalu banyak, atau bekerja pada terlalu banyak tingkat abstraksi secara bersamaan.

  • Memiliki penolong yang tersedia secara global memungkinkan fungsi lain untuk memanggilnya jika ternyata itu bermanfaat secara umum. Jika helper tidak tersedia, Anda tergoda untuk memotong dan menempelkannya, atau melupakannya dan mengimplementasikannya dengan buruk, atau membuat fungsi lain lebih lama dari yang seharusnya.

  • Bersarang fungsi pembantu meningkatkan godaan untuk menggunakan variabel dari ruang lingkup pemanggil tanpa deklarasi, sehingga menjadi tidak jelas apa input dan output dari pembantu itu. Jika suatu fungsi tidak secara jelas menyatakan data apa yang dioperasikannya dan apa efeknya, biasanya itu merupakan pertanda tanggung jawab yang tidak jelas. Dengan mendeklarasikan bantuan, fungsi mandiri memaksa Anda untuk mengetahui apa yang sebenarnya dilakukannya.

Edit

Itu ternyata menjadi pertanyaan yang lebih kontroversial daripada yang saya pikirkan. Untuk memperjelas:

Dalam JavaScript, fungsi spanning file besar sering memenuhi peran kelas karena bahasa tidak menyediakan mekanisme pembatasan ruang lingkup lainnya. Tentu saja fungsi pembantu harus masuk ke dalam kelas semu seperti itu, bukan di luarnya.

Dan titik tentang mengandaikan reuse mudah bahwa jika sebuah sub rutin tidak menjadi lebih banyak digunakan, Anda bersedia untuk memindahkannya keluar dari tempat sama sekali dan meletakkannya di tempat yang tepat, misalnya perpustakaan tali utilitas atau ke dalam registri konfigurasi global Anda. Jika Anda tidak ingin memesan kode seperti itu, maka sebaiknya Anda membuat sarang subrutin, sama seperti yang Anda lakukan dengan Obyek Metode normal dalam bahasa yang lebih "gumpal".

Kilian Foth
sumber
Terima kasih, semua poin yang sangat bagus. Di samping catatan, bagaimana seharusnya saya umumnya memesan fungsi saya? Sebagai contoh, saat ini, dengan program kecil saya hanya memesannya secara alfabet. Tetapi haruskah saya mengelompokkan fungsi pembantu dan pemanggil bersama? Saya kira saya setidaknya harus memecah fungsi-fungsi saya berdasarkan bagian aplikasi mana yang mereka terapkan, misalnya?
Gary
Saya belum pernah repot-repot menentukan definisi metode dalam waktu yang lama. Jika Anda memprogram dengan tingkat profesionalisme apa pun, Anda harus memiliki lingkungan yang memungkinkan Anda melompat ke definisi apa pun dengan sedikit penekanan tombol. Oleh karena itu metode pendek berguna, tetapi file kelas pendek atau bahkan yang disusun dengan baik menambah nilai kecil. (Tentu saja, JavaScript dulu mengharuskan semua definisi ditempatkan di atas penggunaannya atau hasilnya secara teknis akan menjadi perilaku yang tidak terdefinisi - tidak dapat mengingat apakah itu masih terjadi atau tidak.)
Kilian Foth
2
Ini jawaban yang bagus. Masalah enkapsulasi yang rusak adalah sesuatu yang banyak tidak dipertimbangkan ketika menggunakan fungsi dalam.
sbichenko
2
Global adalah bau kode. Mereka menyebabkan kopling yang tidak perlu yang mencegah refactoring, mereka menyebabkan konflik nama, mereka mengekspos detail implementasi, dll. Ini terutama buruk di Javascript, karena semua variabel bisa berubah, kode biasanya dimuat langsung dari pihak ketiga, tidak ada (belum) sistem modul yang tersedia secara luas, atau bahkan implementasi "let" yang tersedia secara luas. Dalam lingkungan yang bermusuhan seperti itu, satu - satunya strategi defensif yang kita miliki adalah enkapsulasi di dalam fungsi satu-tembakan.
Warbo
1
"Memenuhi peran kelas" sedang mencoba untuk menulis JS seolah-olah itu sesuatu yang lain. Apa itu "peran kelas"? Jenis-memeriksa? Modularitas? Kompilasi bertahap? Warisan? Pertanyaannya mencakup pelingkupan, jadi saya berasumsi maksud Anda pelingkupan kasar dari kelas. Itu hanya melewati tanggung jawab: bagaimana kelas harus dicakup? PHP menjadikannya global, yang tidak menyediakan enkapsulasi. Python lingkup kelas secara leksikal, tetapi jika Anda berada di blok dengan cakupan leksikal, mengapa membungkus segala sesuatu di dalam yang lain, blok lingkup kelas? JS tidak memiliki redundansi seperti itu: cukup gunakan cakupan leksikal yang Anda butuhkan.
Warbo
8

Saya akan mengusulkan jalur ketiga, untuk menempatkan kedua fungsi dalam penutupan. Itu akan terlihat seperti:

var functionA = (function(){
    function functionB() {
        // do stuff...
    }

    function functionA() {
        // do stuff...
        functionB();
        // do stuff...
    }

    return functionA;
})();

Kami membuat penutupan dengan membungkus deklarasi kedua fungsi dalam IIFE . Nilai pengembalian IIFE adalah fungsi publik, disimpan dalam variabel nama untuk fungsi tersebut. Fungsi publik dapat dipanggil dengan cara yang persis sama seolah-olah dinyatakan sebagai fungsi global, yaitu functionA(). Perhatikan bahwa nilai kembali adalah fungsi , bukan panggilan ke fungsi, sehingga tidak ada paren pada akhirnya.

Dengan membungkus kedua fungsi seperti itu, functionBsekarang benar-benar pribadi, dan tidak dapat diakses di luar penutupan, tetapi hanya dapat dilihat oleh functionA. Ini tidak mengacaukan namespace global, dan tidak mengacaukan definisi functionA.

cbojar
sumber
0

Mendefinisikan fungsi B dalam fungsi A memberi fungsi B akses ke variabel internal A.

Jadi, fungsi B yang didefinisikan dalam fungsi A memberi kesan (atau setidaknya kemungkinan) bahwa B menggunakan atau mengubah variabel lokal A. Sementara mendefinisikan B di luar A memperjelas bahwa B tidak.

Oleh karena itu, untuk kejelasan kode, saya akan mendefinisikan B dalam A hanya jika B perlu mengakses variabel lokal A. Jika B memiliki tujuan yang jelas yang independen terhadap A, saya pasti akan mendefinisikannya di luar A.

Florian F
sumber
-1

Anda tidak harus menyarangkannya, karena jika tidak, fungsi bersarang akan dibuat kembali setiap kali Anda memanggil fungsi luar.

Domenik
sumber
-3

Karena fungsi B tidak digunakan di tempat lain Anda mungkin juga membuatnya menjadi fungsi dalam fungsi A karena itulah satu-satunya alasan mengapa fungsi itu ada.

Jack_9
sumber
1
ini tampaknya tidak menambahkan sesuatu yang substansial atas poin yang dibuat dan dijelaskan dalam 3 jawaban sebelumnya
agas