Fungsi Anonim Mengeksekusi Diri vs Prototipe

26

Di Javascript ada beberapa teknik yang jelas menonjol untuk membuat dan mengelola kelas / ruang nama dalam javascript.

Saya ingin tahu situasi apa yang perlu menggunakan satu teknik vs yang lain. Saya ingin memilih satu dan tetap menggunakannya.

Saya menulis kode perusahaan yang dikelola dan dibagikan di banyak tim, dan saya ingin tahu apa praktik terbaik saat menulis javascript yang bisa dikelola?

Saya cenderung memilih Fungsi Anonim yang Dapat Dilakukan Sendiri, tetapi saya ingin tahu apa suara masyarakat mengenai teknik-teknik ini.

Prototipe:

function obj()
{
}

obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();

Fungsi Anonim Menutup Sendiri:

//Self-Executing Anonymous Function 
(function( skillet, $, undefined ) {
    //Private Property
    var isHot = true;

    //Public Property
    skillet.ingredient = "Bacon Strips";

    //Public Method
    skillet.fry = function() {
        var oliveOil;

        addItem( "\t\n Butter \n\t" );
        addItem( oliveOil );
        console.log( "Frying " + skillet.ingredient );
    };

    //Private Method
    function addItem( item ) {
        if ( item !== undefined ) {
            console.log( "Adding " + $.trim(item) );
        }
    }     
}( window.skillet = window.skillet || {}, jQuery ));   
//Public Properties      
console.log( skillet.ingredient ); //Bacon Strips  

//Public Methods 
skillet.fry(); //Adding Butter & Fraying Bacon Strips 

//Adding a Public Property 
skillet.quantity = "12"; console.log( skillet.quantity ); //12   

//Adding New Functionality to the Skillet 
(function( skillet, $, undefined ) {
    //Private Property
    var amountOfGrease = "1 Cup";

    //Public Method
    skillet.toString = function() {
        console.log( skillet.quantity + " " + 
                     skillet.ingredient + " & " + 
                     amountOfGrease + " of Grease" );
        console.log( isHot ? "Hot" : "Cold" );
     };     

}( window.skillet = window.skillet || {}, jQuery ));
//end of skillet definition


try {
    //12 Bacon Strips & 1 Cup of Grease
    skillet.toString(); //Throws Exception 
} catch( e ) {
    console.log( e.message ); //isHot is not defined
}

Saya merasa bahwa saya harus menyebutkan bahwa Fungsi Anonim Mengeksekusi Sendiri adalah pola yang digunakan oleh tim jQuery.

Pembaruan Ketika saya mengajukan pertanyaan ini, saya tidak benar-benar melihat pentingnya apa yang saya coba pahami. Masalah sebenarnya yang dihadapi adalah apakah menggunakan baru atau tidak untuk membuat instance objek Anda atau menggunakan pola yang tidak memerlukan konstruktor / penggunaan newkata kunci.

Saya menambahkan jawaban saya sendiri, karena menurut saya kita harus menggunakan pola yang tidak menggunakan newkata kunci.

Untuk informasi lebih lanjut silakan lihat jawaban saya.

Robotsushi
sumber
1
dapatkah Anda memberikan contoh singkat dari dua teknik yang Anda gambarkan?
Hei
Jangan meremehkan prototipe karena sampel sederhana saya.
Robotsushi
1
itu tidak mengeksekusi sendiri = /
Hei
2
saya tidak melihat tanda kurung untuk menutup ekspresi atau menyebutnya ...
Hei
1
(+1) Ruang nama diabaikan oleh banyak pengembang.
umlcat

Jawaban:

22

Fungsi anonim yang dijalankan sendiri digunakan untuk mengotomatiskan eksekusi skrip tanpa menghubungkan ke peristiwa eksternal (yaitu window.onload).

Dalam contoh ini digunakan untuk membentuk pola Modul klasik, tujuan utamanya adalah untuk memperkenalkan namespace ke lingkungan global, dan menyediakan enkapsulasi untuk properti internal yang tidak "diekspor" atau dilampirkan ke namespace.

Memodifikasi prototipe objek, di sisi lain, digunakan untuk membangun warisan (atau memperpanjang asli). Pola ini digunakan untuk menghasilkan 1: n objek dengan metode atau properti umum.

Anda tidak boleh memilih satu pola dalam preferensi terhadap yang lain, karena mereka melakukan tugas yang berbeda . Dalam hal penempatan nama, Self Executing Function adalah pilihan yang tepat.

sunwukung
sumber
7
Perhatikan bahwa "fungsi anonim yang dijalankan sendiri" umumnya dikenal sebagai ekspresi fungsi yang segera dipanggil (IIFE) .
voithos
Saya menghindari mereka dan sejujurnya saya tidak mendapatkan cinta dengan IIFEs. Mereka berantakan untuk men-debug dan memecah garis kode di Eclipse. Jika Anda memerlukan namespace tempelkan pada objek, jika Anda perlu menjalankannya, panggil saja, dan enkapsulasi tidak benar-benar memberi saya apa-apa.
Daniel Sokolowski
4

Inilah pola yang baru saja saya mulai gunakan (telah menggunakan variasi hingga kemarin):

function MyClass() {
    // attributes
    var privateVar = null;

    // function implementations
    function myPublicFunction() {
    }

    function myPrivateFunction() {
    }

    // public declarations
    this.myPublicFunction = myPublicFunction;
}

MyClass.prototype = new ParentClass(); // if required

Beberapa pemikiran tentang ini:

  1. Anda seharusnya tidak mendapatkan (anonymous)jejak di jejak tumpukan debugger Anda karena semuanya bernama (tidak ada fungsi anonim).
  2. Ini adalah pola terbersih yang pernah saya lihat
  3. Anda dapat dengan mudah mengelompokkan API Anda yang terbuka tanpa menerapkan implementasinya ke deklarasi (artinya seseorang dapat dengan mudah mengakses antarmuka kelas publik Anda tanpa harus menggulir)

Satu-satunya waktu yang saya gunakan prototypelagi adalah mendefinisikan warisan.

Demian Brecht
sumber
5
Ada beberapa masalah dengan ini. Objek fungsi baru dibuat untuk setiap "metode" dengan setiap permintaan konstruktor. Juga, memanggil konstruktor untuk mendapatkan salinan objek prototipe untuk warisan disukai. Gunakan Object.create (ParentClass.prototype), atau shim untuk Object.create sepertifunction clone(obj){return this typeof 'clone' ? this : new clone(clone.prototype=obj)}
Hei
@GGG: Ya, Anda benar dengan poin pertama Anda. Saya akan mengatakan (dan seharusnya disebutkan dalam posting saya) bahwa setiap kasus penggunaan spesifik suatu implementasi harus dipikirkan. Masalah saya dengan prototypemetode seperti yang Anda sarankan adalah (kecuali ada metode yang saya tidak terbiasa, yang mungkin terjadi) bahwa Anda kehilangan kemampuan untuk merangkum atribut, yang berarti semuanya terbuka sebagai publik (yang bukan akhir dari dunia , hanya preferensi pribadi).
Demian Brecht
Juga, setelah melihat pekerjaan berikut yang dilakukan pada jsperf ( jsperf.com/object-create-vs-constructor-vs-object-literal/12 ), saya akan mengambil peningkatan kinerja dari biaya memori salinan tambahan hampir setiap hari (sekali lagi, sangat subyektif dengan use case khusus).
Demian Brecht
Sekarang, setelah mengatakan semua itu, saya hanya setengah jalan melalui ECMA-262, jadi mungkin ada banyak hal yang saya tidak lihat .. Juga, saya tidak mengambil kata Crockford sebagai Injil .. Ya, dia salah dari para ahli di lapangan (salah satu yang paling utama tentu saja), tetapi itu tidak selalu membuatnya 100% benar setiap saat. Ada ahli lain di luar sana (saya tidak menjadi salah satu dari mereka;)) yang memiliki pendapat yang bertentangan dengan argumen yang meyakinkan.
Demian Brecht
2
Anda mungkin tertarik pada hal ini yang sedang saya kerjakan .
Hei
3

Saya menggunakan prototipe karena lebih bersih dan mengikuti pola pewarisan standar. Fungsi self-invaging sangat bagus untuk pengembangan browser atau situasi di mana Anda tidak tahu di mana kode dieksekusi, tetapi selain itu hanya noise.

Contoh:

var me;

function MyObject () {
    this.name = "Something";
}

MyObject.prototype.speak = function speak () {
    return "Hello, my name is " + this.name;
};

me = new MyObject();
me.name = "Joshua";
alert(me.speak());
Josh K.
sumber
1
Menjalankan fungsi anonim sangat berguna untuk memungkinkan Anda memiliki fungsi pribadi yang dapat diakses ke kelas.
Zee
1

Saya akan menggunakan fungsi self-executing, tetapi dengan sedikit perbedaan:

MyClass = (function() {
     var methodOne = function () {};
     var methodTwo = function () {};
     var privateProperty = "private";
     var publicProperty = "public";

     return function MyClass() {
         this.methodOne = methodOne;
         this.methodTwo = methodTwo;
         this.publicProperty = publicProperty;
     };
})();

Jika menemukan pendekatan ini jauh lebih bersih, karena saya memisahkan variabel global yang dikembalikan dari setiap parameter input (seperti jQuery) (cara Anda menulisnya sama dengan mengembalikan batal dan menggunakan parameter ref di C #, yang saya temukan sedikit mati, atau meneruskan pointer ke pointer dan menugaskannya kembali di C ++). Jika saya kemudian akan melampirkan metode atau properti tambahan ke kelas saya akan menggunakan pewarisan prototypal (contoh dengan metode $ .extend jQuery, tapi cukup mudah untuk menggulung perpanjangan Anda sendiri ()):

var additionalClassMethods = (function () {
    var additionalMethod = function () { alert('Test Method'); };
    return { additionalMethod: additionalMethod };
})();

$.extend(MyClass.prototype, additionalClassMethods);

var m = new MyClass();
m.additionalMethod(); // Pops out "Test Method"

Dengan cara ini Anda memiliki perbedaan yang jelas antara metode yang ditambahkan dan yang asli.

Ed James
sumber
1
Apakah saya satu-satunya yang berpikir menggunakan NFE seperti ini adalah ide yang buruk?
Hei
1

Contoh Langsung

(function _anonymouswrapper(undefined) {

    var Skillet = {
        constructor: function (options) {
            options && extend(this, options);
            return this; 
        },
        ingredient: "Bacon Strips",
        _isHot: true,
        fry: function fry(oliveOil) {
            this._addItem("\t\n Butter \n\t");
            this._addItem(oliveOil);
            this._addItem(this.ingredient);
            console.log("Frying " + this.ingredient);
        },
        _addItem: function addItem(item) {
            console.log("Adding " + item.toString().trim());
        }
    };

    var skillet = Object.create(Skillet).constructor();

    console.log(skillet.ingredient);
    skillet.fry("olive oil");

    var PrintableSkillet = extend(Object.create(Skillet), {
        constructor: function constructor(options) {
            options && extend(this, options);
            return this;
        },
        _amountOfGrease: "1 Cup",
        quantity: 12,
        toString: function toString() {
            console.log(this.quantity + " " +
                        this.ingredient + " & " +
                        this._amountOfGrease + " of Grease");
            console.log(this._isHot ? "Hot" : "Cold");
        }
    });

    var skillet = Object.create(PrintableSkillet).constructor();

    skillet.toString();

    function extend(target, source) {
        Object.getOwnPropertyNames(source).forEach(function (name) {
            var pd = Object.getOwnPropertyDescriptor(source, name);
            Object.defineProperty(target, name, pd);
        });
        return target;
    }
}());

Anda dapat menggunakan IIFE untuk meniru "lingkup modul" di sekitar kode Anda. Maka Anda bisa menggunakan objek seperti biasa.

Jangan "meniru" negara swasta menggunakan penutupan karena memiliki hukuman memori yang besar.

Jika Anda menulis aplikasi perusahaan dan ingin menjaga penggunaan memori Anda di bawah 1GB, hindari penggunaan penutupan yang tidak perlu untuk menyimpan status.

Raynos
sumber
Anda memiliki beberapa kesalahan ketik pada pasangan kode. Saya tidak positif tentang pernyataan penutupan Anda yang secara otomatis menyebabkan penggunaan memori yang berlebihan juga, saya pikir itu tergantung pada seberapa banyak Anda menggunakannya dan seberapa baik Anda berurusan dengan masalah pelingkupan (pencampuran hal-hal dari dalam penutupan dengan hal-hal dari luar umumnya buruk, misalnya (penggunaan konsol global Anda adalah contoh yang baik untuk hal ini, kode di atas akan jauh lebih efisien jika Anda lulus dalam konsol sebagai variabel)).
Ed James
@ Edwoodcock Saya pikir kode itu buggy, hanya refactored dan memperbaikinya.
Raynos
@EdWoodcock perbedaan efisiensi antara lokal consoledan global consoleadalah optimasi mikro. Layak untuk dilakukan sehingga Anda dapat meminimalkan consoletetapi itu masalah yang berbeda
Raynos
@EdWoodcock penutupan lambat jika objek duplikat Anda di dalamnya. Yang lambat adalah membuat fungsi di dalam fungsi saat tidak diperlukan. Penutupan juga memiliki overhead memori kecil untuk menyimpan keadaan dibandingkan dengan menyimpan keadaan pada objek secara langsung
Raynos
Ya, hanya ingin menunjukkannya karena jawaban Anda adalah cara yang masuk akal untuk menyelesaikan banyak hal (walaupun bukan cara saya memilih untuk menggunakan). (Saya telah melakukan edit tetapi saya masih tidak yakin tentang etiket orang-orang di situs SE khusus ini).
Ed James
0

Pembaruan Sekarang saya memiliki pemahaman yang jauh lebih baik tentang javascript dan merasa saya dapat menjawab pertanyaan dengan benar. Saya pikir itu adalah topik javascript yang buruk, tetapi sangat penting untuk diatasi.

Pola Self Executing Anonymous Function bukan pola yang mengharuskan penggunaan kata kunci baru jika Anda menghindari penggunaan di luar fungsi ini. Saya setuju dengan gagasan bahwa menggunakan yang baru adalah teknik lama dan kita harus berusaha menggunakan pola yang menghindari penggunaan yang baru.

Fungsi Self Executing Anonymous memenuhi kriteria ini.

Jawaban atas pertanyaan ini adalah subyektif, karena ada begitu banyak gaya pengkodean dalam javascript. Namun berdasarkan penelitian dan pengalaman saya, saya akan merekomendasikan memilih untuk menggunakan Fungsi Anonim Mengeksekusi Sendiri untuk menentukan API Anda dan menghindari penggunaan baru bila memungkinkan.

Robotsushi
sumber
1
Apa yang buruk tentang kata kunci baru? Saya penasaran.
Ally
0

Inilah bagaimana saya akan melakukan ini IIFE SelfExecutingFunctionuntuk memperpanjang Myclassdengan Myclass.anotherFunction();

MyClass = (function() {
     var methodOne = function () {};
     var methodTwo = function () {};
     var privateProperty = "private";
     var publicProperty = "public";

    //Returning the anonymous object {} all the methods and properties are reflected in Myclass constructor that you want public those no included became hidden through closure; 
     return {
         methodOne = methodOne;
         methodTwo = methodTwo;
         publicProperty = publicProperty;
     };
})();

@@@@@@@@@@@@@@@@@@@@@@@@
//then define another IIFE SEF to add var function=anothermethod(){};
(function(obj){
return obj.anotherfunction=function(){console.log("Added to Myclass.anotherfunction");};
})(Myclass);

//the last bit : (function(obj){})(Myclass); obj === Myclass obj is an alias to pass the Myclass to IIFE

var myclass = new Myclass();
myclass.anotherfunction(); //"Added to Myclass.anotherfunction"
oneLINE
sumber
1
Programmer adalah tur pertanyaan konseptual jawaban yang diharapkan menjelaskan hal-hal. Melempar kesedihan alih-alih penjelasannya seperti menyalin kode dari IDE ke papan tulis: mungkin terlihat biasa dan bahkan terkadang dapat dimengerti, tetapi rasanya aneh ... hanya aneh. Papan tulis tidak memiliki kompiler
nyamuk