Apakah cara mendefinisikan objek JS ini memiliki tujuan?

87

Saya mempertahankan beberapa kode warisan dan saya perhatikan bahwa pola berikut untuk mendefinisikan objek digunakan:

var MyObject = {};

(function (root) {

    root.myFunction = function (foo) {
        //do something
    };

})(MyObject);

Apakah ada tujuan dari ini? Apakah itu setara dengan hanya melakukan hal berikut?

var MyObject = {

    myFunction : function (foo) {
        //do something
    };

};

Saya tidak akan memulai misi suci untuk merefaktor seluruh basis kode sesuai dengan keinginan saya, tetapi saya benar-benar ingin memahami alasan di balik cara memutar untuk menentukan objek.

Terima kasih!

Sebastián Vansteenkiste
sumber
1
Dalam contoh persis Anda tidak ada perbedaan. Jika Anda mengembangkannya, mungkin ada perbedaan tetapi kemudian akan ada pendekatan berbeda yang ikut bermain juga.
Travis J
Tidak ada bedanya, objek diteruskan sebagai salinan referensi, jadi bahkan ketika mendefinisikan myFunction di dalam IIFE, itu masih dapat diakses di luarnya.
adeneo
1
@adeneo Bukan untuk contoh ini, by myFunctionbisa menggunakan beberapa variabel yang didefinisikan di luar dirinya sendiri yang tidak akan dapat diakses dari luar. Lihat jawaban saya
Juan Mendes
2
kemungkinan duplikat dari Apa nama pola JavaScript ini dan mengapa itu digunakan? (tidak yakin apakah saya harus menutup). Lihat juga Deklarasi Ruang Nama JavaScript atau yang ini .
Bergi

Jawaban:

116

Ini disebut pola modul http://toddmotto.com/mastering-the-module-pattern/

Alasan utamanya adalah Anda membuat metode dan variabel yang benar-benar pribadi. Dalam kasus Anda, ini tidak berarti karena tidak menyembunyikan detail implementasi apa pun.

Berikut adalah contoh di mana masuk akal untuk menggunakan pola modul.

var MyNameSpace = {};

(function(ns){
    // The value variable is hidden from the outside world
    var value = 0;

    // So is this function
    function adder(num) {
       return num + 1;
    }

    ns.getNext = function () {
       return value = adder(value);
    }
})(MyNameSpace);

var id = MyNameSpace.getNext(); // 1
var otherId = MyNameSpace.getNext(); // 2
var otherId = MyNameSpace.getNext(); // 3

Sedangkan jika Anda hanya menggunakan benda lurus, adderdan valueakan menjadi publik

var MyNameSpace = {
    value: 0,
    adder: function(num) {
       return num + 1;
    },
    getNext: function() {
       return this.value = this.adder(this.value);
    }
}

Dan Anda bisa memecahkannya dengan melakukan hal-hal seperti

MyNameSpace.getNext(); // 1
MyNameSpace.value = 0;
MyNameSpace.getNext(); // 1 again
delete MyNameSpace.adder;
MyNameSpace.getNext(); // error undefined is not a function

Tetapi dengan versi modul

MyNameSpace.getNext(); // 1
 // Is not affecting the internal value, it's creating a new property
MyNameSpace.value = 0;
MyNameSpace.getNext(); // 2, yessss
// Is not deleting anything
delete MyNameSpace.adder;
MyNameSpace.getNext(); // no problemo, outputs 3
Juan Mendes
sumber
2
Ini tidak benar-benar menjawab pertanyaan tentang apa perbedaan antara kedua alternatif tersebut?
20
@torazaburo Contoh OP bukanlah contoh yang baik, saya telah memberikan contoh nyata yang menunjukkan kapan harus menggunakan pola modul.
Juan Mendes
Ini ns.getNext: function () {tidak akan bisa dikompilasi.
punund
Saya akan melakukannya, jika saya yakin bagaimana memperbaikinya. Saya pikir ada beberapa aturan untuk dicegah delete MyNameSpace.getNext.
punund
2
@punund JS tidak memiliki kompiler yang memiliki penerjemah:)
frogatto
22

Tujuannya adalah untuk membatasi aksesibilitas fungsi dalam closure untuk membantu mencegah skrip lain mengeksekusi kode di dalamnya. Dengan membungkusnya di sekitar closure, Anda mendefinisikan ulang cakupan eksekusi untuk semua kode di dalam closure dan secara efektif membuat ruang lingkup privat. Lihat artikel ini untuk info lebih lanjut:

http://lupomontero.com/using-javascript-closures-to-create-private-scopes/

Dari artikel:

Salah satu masalah paling terkenal di JavaScript adalah ketergantungannya pada cakupan global, yang pada dasarnya berarti bahwa variabel apa pun yang Anda deklarasikan di luar fungsi berada dalam ruang nama yang sama: objek jendela yang tidak menyenangkan. Karena sifat halaman web, banyak skrip dari sumber yang berbeda dapat (dan akan) berjalan pada halaman yang sama dengan cakupan global yang sama dan ini bisa menjadi hal yang sangat buruk karena dapat menyebabkan benturan nama (variabel dengan nama yang sama sedang ditimpa) dan masalah keamanan. Untuk meminimalkan masalah, kita dapat menggunakan closure JavaScript yang kuat untuk membuat lingkup pribadi tempat kita dapat memastikan variabel kita tidak terlihat oleh skrip lain di halaman.



Kode:

var MyObject = {};

(function (root) {
    function myPrivateFunction() {
       return "I can only be called from within the closure";
    }

    root.myFunction = function (foo) {
        //do something
    };    

    myPrivateFunction(); // returns "I can only be called from within the closure"

})(MyObject);


myPrivateFunction(); // throws error - undefined is not a function
Jonathan Crowe
sumber
1
myFunctiontidak dalam cakupan global di versi kedua. Tujuan sebenarnya adalah untuk menyediakan ruang lingkup di mana fungsi pembantu internal dapat didefinisikan.
Barmar
myFunctionberada dalam cakupan global karena didefinisikan dalam objek global myObject. Pada versi kedua, kode lain dalam aplikasi dapat dijalankan myFunction. Dalam versi pertama, hanya kode dalam penutupan yang memiliki akses kemyFunction
Jonathan Crowe
Tidak, myFunctionhanya dapat dijalankan sebagai MyObject.myFunction(), sama seperti versi pertama.
Barmar
@JonathanCrowe Contoh OP bukanlah contoh yang baik, itu mengekspos semua yang ada di dalam modul, jadi ya, itu bisa diakses di luar. Lihat jawaban saya untuk kasus modul yang berguna
Juan Mendes
@JuanMendes poin yang bagus, contoh OP bukanlah penggunaan yang baik dari pola modul
Jonathan Crowe
6

keuntungan:

  1. memelihara variabel dalam ruang lingkup pribadi.

  2. Anda dapat memperluas fungsionalitas objek yang ada.

  3. kinerja meningkat.

Saya pikir tiga poin sederhana di atas cukup untuk mengikuti aturan tersebut. Dan untuk membuatnya tetap sederhana tidak lain adalah menulis fungsi batin.

Mateen
sumber
6

Dalam kasus tertentu yang Anda tunjukkan, tidak ada perbedaan yang berarti, dalam hal fungsionalitas atau visibilitas.

Sepertinya pembuat kode asli mengadopsi pendekatan ini sebagai semacam templat yang memungkinkannya untuk menentukan variabel privat yang dapat digunakan dalam definisi hal-hal seperti myFunction:

var MyObject = {};
(function(root) {
    var seconds_per_day = 24 * 60 * 60;   // <-- private variable
    root.myFunction = function(foo) {
        return seconds_per_day;
    };
})(MyObject);

Ini menghindari penghitungan seconds_per_daysetiap kali fungsi dipanggil, sekaligus menjaganya agar tidak mencemari ruang lingkup global.

Namun, pada dasarnya tidak ada yang berbeda dari itu dan hanya mengatakan

var MyObject = function() {
    var seconds_per_day = 24 * 60 * 60;
    return {
        myFunction: function(foo) {
            return seconds_per_day;
        }
    };
}();

Pembuat kode asli mungkin lebih suka untuk dapat menambahkan fungsi ke objek menggunakan sintaks deklaratif root.myFunction = function, daripada sintaks objek / properti myFunction: function. Tetapi perbedaan itu terutama adalah masalah preferensi.

Namun, struktur yang diambil oleh pembuat kode asli memiliki keuntungan bahwa properti / metode dapat dengan mudah ditambahkan di tempat lain dalam kode:

var MyObject = {};
(function(root) {
    var seconds_per_day = 24 * 60 * 60;
    root.myFunction = function(foo) {
        return seconds_per_day;
    };
})(MyObject);

(function(root) {
    var another_private_variable = Math.pi;
    root.myFunction2 = function(bar) { };
})(MyObject);

Intinya, tidak perlu mengadopsi pendekatan ini jika Anda tidak perlu, tetapi juga tidak perlu mengubahnya, karena ini bekerja dengan baik dan sebenarnya memiliki beberapa keuntungan.


sumber
6
  1. Pola pertama dapat digunakan sebagai modul yang mengambil objek dan mengembalikan objek tersebut dengan beberapa modifikasi. Dengan kata lain, Anda dapat mendefinisikan modul seperti berikut.

    var module = function (root) {
        root.myFunction = function (foo) {
            //do something
        };
    }
    

    Dan gunakan seperti:

    var obj = {};
    module(obj);
    

    Jadi keuntungan bisa menjadi kegunaan kembali modul ini untuk digunakan nanti.


  1. Pada pola pertama, Anda dapat menentukan ruang lingkup pribadi untuk menyimpan barang pribadi Anda seperti properti dan metode pribadi. Misalnya, pertimbangkan cuplikan ini:

    (function (root) {
    
        // A private property
        var factor = 3;
    
        root.multiply = function (foo) {
            return foo * factor;
        };
    })(MyObject);
    

  1. Pola ini dapat digunakan untuk menambahkan metode atau properti ke semua jenis objek seperti array, literal objek, fungsi.

    function sum(a, b) {
        return a + b;
    }
    
    (function (root) {
        // A private property
        var factor = 3;
        root.multiply = function (foo) {
            return foo * factor;
        };
    })(sum);
    
    console.log(sum(1, 2)); // 3
    console.log(sum.multiply(4)); // 12
    

Menurut saya, keuntungan utama bisa jadi yang kedua (menciptakan ruang lingkup pribadi)

frogatto.dll
sumber
5

Pola ini menyediakan cakupan di mana Anda dapat menentukan fungsi pembantu yang tidak terlihat dalam cakupan global:

(function (root) {

    function doFoo() { ... };

    root.myFunction = function (foo) {
        //do something
        doFoo();
        //do something else
    };

})(MyObject);

doFoo bersifat lokal untuk fungsi anonim, tidak dapat dirujuk dari luar.

Barmar
sumber