Apa nama pola JavaScript ini dan mengapa itu digunakan?

100

Saya mempelajari THREE.js dan melihat pola di mana fungsi didefinisikan seperti ini:

var foo = ( function () {
    var bar = new Bar();

    return function ( ) {
        //actual logic using bar from above.
        //return result;
    };
}());

(Contoh lihat metode raycast di sini ).

The biasa variasi metode tersebut akan terlihat seperti ini:

var foo = function () {
    var bar = new Bar();

    //actual logic.
    //return result;
};

Membandingkan versi pertama dengan variasi normal , versi pertama tampaknya berbeda dalam hal:

  1. Ini memberikan hasil dari fungsi yang mengeksekusi sendiri.
  2. Ini mendefinisikan variabel lokal dalam fungsi ini.
  3. Ini mengembalikan fungsi sebenarnya yang berisi logika yang menggunakan variabel lokal.

Jadi perbedaan utamanya adalah pada variasi pertama bilah hanya ditetapkan satu kali, saat inisialisasi, sedangkan variasi kedua membuat variabel sementara ini setiap kali dipanggil.

Tebakan terbaik saya tentang mengapa ini digunakan adalah membatasi jumlah contoh untuk bar (hanya akan ada satu) dan dengan demikian menghemat overhead manajemen memori.

Pertanyaan saya:

  1. Apakah asumsi ini benar?
  2. Apakah ada nama untuk pola ini?
  3. Mengapa ini digunakan?
Patrick Klug
sumber
1
@Cukup adil. Saya menandainya sebagai THREE.js karena saya pikir kontributor THREE.js adalah yang paling memenuhi syarat untuk menjawab ini tetapi ya, ini adalah pertanyaan JS umum.
Patrick Klug
2
Saya percaya itu disebut penutupan. Anda bisa membaca tentang mereka.
StackFlowed
1
Jika ini adalah satu-satunya tempat Bar dibuat, maka itu adalah pola tunggal .
Paul
8
Tidak harus menghemat memori, tetapi dapat menyimpan status di seluruh pemanggilan
Juan Mendes
2
@wrongAnswer: tidak persis. di sini fungsi anonim (yang akan menjadi penutupan) dijalankan dengan segera.
njzk2

Jawaban:

100

Asumsi Anda hampir benar. Mari kita ulas dulu.

  1. Ini menetapkan kembalinya fungsi yang mengeksekusi sendiri

Ini disebut ekspresi fungsi yang dipanggil segera atau IIFE

  1. Ini mendefinisikan variabel lokal dalam fungsi ini

Ini adalah cara memiliki bidang objek pribadi di JavaScript karena tidak menyediakan privatekata kunci atau fungsionalitas sebaliknya.

  1. Ini mengembalikan fungsi sebenarnya yang berisi logika yang menggunakan variabel lokal.

Sekali lagi, poin utamanya adalah variabel lokal ini bersifat privat .

Apakah ada nama untuk pola ini?

AFAIK Anda dapat memanggil Pola Modul pola ini . Mengutip:

Pola Modul merangkum "privasi", status, dan organisasi menggunakan closure. Ini menyediakan cara untuk membungkus campuran metode dan variabel publik dan privat, melindungi potongan agar tidak bocor ke ruang lingkup global dan secara tidak sengaja bertabrakan dengan antarmuka pengembang lain. Dengan pola ini, hanya API publik yang dikembalikan, menjaga semua yang lain di dalam closure bersifat pribadi.

Membandingkan kedua contoh tersebut, tebakan terbaik saya tentang mengapa yang pertama digunakan adalah:

  1. Ini menerapkan pola desain Singleton.
  2. Seseorang dapat mengontrol cara suatu objek dari tipe tertentu dapat dibuat menggunakan contoh pertama. Satu kecocokan dengan poin ini adalah metode pabrik statis seperti yang dijelaskan di Java Efektif.
  3. Ini efisien jika Anda membutuhkan status objek yang sama setiap saat.

Tetapi jika Anda hanya membutuhkan objek vanilla setiap saat, maka pola ini mungkin tidak akan menambah nilai apa pun.

MD. Sahib Bin Mahboob
sumber
1
1 untuk mengidentifikasi pola dengan benar dari buku Addy Osmani. Anda benar dalam penamaan Anda - ini memang pola modul - pola modul yang terbuka dengan cara itu.
Benjamin Gruenbaum
4
Saya setuju dengan jawaban Anda, kecuali bagian 'no out-of-the-box private variable'. Semua variabel JS secara leksikal dicakup 'out-of-the-box', yang merupakan mekanisme yang lebih kuat / umum daripada variabel "pribadi" (misalnya seperti yang ditemukan di Java); oleh karena itu JS mendukung variabel "pribadi" sebagai kasus khusus dari cara menangani semua variabel.
Warbo
Saya pikir dengan "variabel pribadi", yang Anda maksud adalah "bidang objek" pribadi
Ian Ringrose
1
Hanya untuk detailnya: klauskomenda.com/code/javascript-programming-patterns/#module
Nagaraj Tantri
4
@LukaHorvat: sebenarnya, javascript tidak lebih "kuat" dari bahasa lain (saya lebih suka istilah ekspresif). Sebenarnya, ini kurang ekspresif, karena satu-satunya cara untuk melindungi variabel Anda adalah menyertakannya dalam fungsi untuk menghindari penggunaan kembali variabel yang tidak masuk akal. Pola Modul adalah syarat pasti untuk membuat kode javascript yang baik, tetapi ini bukan fitur bahasa, ini lebih merupakan solusi yang menyedihkan untuk menghindari tergigit oleh kelemahan bahasa.
Falanwe
11

Ini membatasi biaya inisialisasi objek dan juga memastikan bahwa semua pemanggilan fungsi menggunakan objek yang sama . Ini memungkinkan, misalnya, status disimpan di objek untuk digunakan dalam pemanggilan di masa mendatang.

Meskipun mungkin membatasi penggunaan memori, biasanya GC akan mengumpulkan objek yang tidak digunakan, jadi pola ini sepertinya tidak akan banyak membantu.

Pola ini adalah bentuk penutupan tertentu .

Fengyang Wang
sumber
1
Di JS biasanya disebut sebagai 'modul'
Lesha Ogonkov
2
Saya tidak akan menyebutnya "bentuk penutupan tertentu", per se. Ini adalah pola yang menggunakan penutupan. Nama polanya masih diperebutkan.
Chris Hayes
4
Apakah itu benar-benar membutuhkan nama? Apakah semuanya harus berpola? Apakah kita benar-benar membutuhkan taksonomi tak terbatas dari varian "pola modul"? Tidak bisakah itu hanya menjadi "IIFE dengan beberapa variabel lokal yang mengembalikan fungsi?"
Dagg Nabbit
3
@DaggNabbit Ketika pertanyaannya adalah "apa yang disebut pola ini"? Ya, itu membutuhkan nama atau argumen yang meyakinkan yang tidak ada. Selain itu, pola ada karena suatu alasan. Saya tidak mengerti mengapa Anda mencerca mereka di sini.
Chris Hayes
4
@ChrisHayes jika perlu nama, mengapa Anda perlu membuat argumen bahwa ia tidak memilikinya? Itu tidak masuk akal. Jika perlu, pasti tidak ada. Saya tidak punya masalah dengan pola, tapi menurut saya tidak perlu mengklasifikasikan setiap idiom sederhana sebagai pola. Melakukan hal itu mengarah pada pemikiran dengan cara terbatas ("Apakah ini pola modul? Apakah saya menggunakan pola modul dengan benar?" Vs. "Saya memiliki IIFE dengan beberapa variabel lokal yang mengembalikan fungsi, apakah desain ini berfungsi untuk saya?")
Dagg Nabbit
8

Saya tidak yakin apakah pola ini memiliki nama yang lebih benar, tetapi ini tampak seperti modul bagi saya, dan alasan penggunaannya adalah untuk merangkum dan mempertahankan status.

Penutupan (diidentifikasi oleh fungsi di dalam fungsi) memastikan bahwa fungsi dalam memiliki akses ke variabel di dalam fungsi luar.

Dalam contoh yang Anda berikan, fungsi bagian dalam dikembalikan (dan ditetapkan ke foo) dengan menjalankan fungsi luar yang berarti tmpObjectterus hidup di dalam closure dan beberapa panggilan ke fungsi bagian dalam foo()akan beroperasi pada contoh yang sama tmpObject.

itsmejodie
sumber
5

Perbedaan utama antara kode Anda dan kode Three.js adalah bahwa dalam kode Three.js variabel tmpObjecthanya diinisialisasi sekali, dan kemudian dibagikan oleh setiap pemanggilan fungsi yang dikembalikan.

Ini akan berguna untuk menjaga beberapa status antar panggilan, mirip dengan bagaimana staticvariabel digunakan dalam bahasa mirip C.

tmpObject adalah variabel privat yang hanya dapat dilihat oleh fungsi bagian dalam.

Ini mengubah penggunaan memori, tetapi tidak dirancang untuk menghemat memori.

mcfedr.dll
sumber
5

Saya ingin berkontribusi pada utas yang menarik ini dengan memperluas konsep pola modul pengungkapan, yang memastikan bahwa semua metode dan variabel dijaga kerahasiaannya hingga mereka diekspos secara eksplisit.

masukkan deskripsi gambar di sini

Dalam kasus terakhir, metode penambahan akan disebut sebagai Calculator.add ();

carlodurso.dll
sumber
0

Dalam contoh yang diberikan, cuplikan pertama akan menggunakan contoh tmpObject yang sama untuk setiap panggilan ke fungsi foo (), sedangkan seperti pada cuplikan kedua, tmpObject akan menjadi contoh baru setiap saat.

Salah satu alasan cuplikan pertama mungkin telah digunakan, adalah karena variabel tmpObject dapat dibagikan di antara panggilan ke foo (), tanpa nilainya bocor ke dalam cakupan di mana foo () dideklarasikan.

Versi fungsi yang tidak segera dijalankan dari cuplikan pertama sebenarnya akan terlihat seperti ini:

var tmpObject = new Bar();

function foo(){
    // Use tmpObject.
}

Namun perlu diperhatikan bahwa versi ini memiliki tmpObject dalam lingkup yang sama dengan foo (), sehingga dapat dimanipulasi nanti.

Cara yang lebih baik untuk mencapai fungsionalitas yang sama adalah dengan menggunakan modul terpisah:

Modul 'foo.js':

var tmpObject = new Bar();

module.exports = function foo(){
    // Use tmpObject.
};

Modul 2:

var foo = require('./foo');

Perbandingan antara kinerja IEF dan fungsi pembuat foo bernama: http://jsperf.com/ief-vs-named-function

Kory Nunn
sumber
3
Contoh 'lebih baik' Anda hanya berfungsi di NodeJS dan Anda belum menjelaskan bagaimana itu lebih baik.
Benjamin Gruenbaum
Membuat modul terpisah tidaklah "lebih baik", hanya saja berbeda. Secara khusus, ini adalah cara untuk menciutkan fungsi tingkat tinggi ke objek tingkat pertama. Kode orde pertama cenderung lebih mudah untuk dilalui, tetapi umumnya lebih bertele-tele dan memaksa kita untuk merefleksikan hasil antara.
Warbo
@BenjaminGruenbaum Modul tidak hanya di Node, ada banyak solusi modul sisi klien, misalnya, browserify. Saya menganggap solusi modul menjadi "lebih baik" karena lebih mudah dibaca, lebih mudah di-debug, dan lebih eksplisit tentang apa yang ada dalam cakupan, dan di mana.
Kory Nunn