it-swarm-id.com

Apakah penggunaan fungsi anonim mempengaruhi kinerja?

Saya bertanya-tanya, apakah ada perbedaan kinerja antara menggunakan fungsi bernama dan fungsi anonim di Javascript?

for (var i = 0; i < 1000; ++i) {
    myObjects[i].onMyEvent = function() {
        // do something
    };
}

vs.

function myEventHandler() {
    // do something
}

for (var i = 0; i < 1000; ++i) {
    myObjects[i].onMyEvent = myEventHandler;
}

Yang pertama lebih rapi karena tidak mengacaukan kode Anda dengan fungsi yang jarang digunakan, tetapi apakah penting bahwa Anda mendeklarasikan ulang fungsi itu beberapa kali?

80
nickf

Masalah kinerja di sini adalah biaya membuat objek fungsi baru di setiap iterasi dari loop dan bukan fakta bahwa Anda menggunakan fungsi anonim:

for (var i = 0; i < 1000; ++i) {    
    myObjects[i].onMyEvent = function() {
        // do something    
    };
}

Anda membuat seribu objek fungsi yang berbeda walaupun mereka memiliki kode yang sama dan tidak mengikat ke lingkup leksikal ( closure ). Sebaliknya, yang berikut tampaknya lebih cepat, karena ia hanya memberikan referensi fungsi sama ke elemen array di seluruh loop:

function myEventHandler() {
    // do something
}

for (var i = 0; i < 1000; ++i) {
    myObjects[i].onMyEvent = myEventHandler;
}

Jika Anda membuat fungsi anonim sebelum memasukkan loop, maka hanya menetapkan referensi ke elemen array saat di dalam loop, Anda akan menemukan bahwa tidak ada perbedaan kinerja atau semantik apa pun jika dibandingkan dengan versi fungsi yang disebutkan:

var handler = function() {
    // do something    
};
for (var i = 0; i < 1000; ++i) {    
    myObjects[i].onMyEvent = handler;
}

Singkatnya, tidak ada biaya kinerja yang dapat diamati untuk menggunakan anonim atas fungsi yang disebutkan.

Selain itu, mungkin terlihat dari atas bahwa tidak ada perbedaan antara:

function myEventHandler() { /* ... */ }

dan:

var myEventHandler = function() { /* ... */ }

Yang pertama adalah pernyataan fungsi sedangkan yang terakhir adalah penugasan variabel untuk fungsi anonim. Meskipun mereka tampaknya memiliki efek yang sama, JavaScript memperlakukan mereka sedikit berbeda. Untuk memahami perbedaannya, saya sarankan membaca, “ Ambiguitas deklarasi fungsi JavaScript ”.

Waktu eksekusi aktual untuk setiap pendekatan sebagian besar akan ditentukan oleh implementasi browser dari kompiler dan runtime. Untuk perbandingan lengkap kinerja browser modern, kunjungi situs JS Perf

81
Atif Aziz

Ini kode pengujian saya:

var dummyVar;
function test1() {
    for (var i = 0; i < 1000000; ++i) {
        dummyVar = myFunc;
    }
}

function test2() {
    for (var i = 0; i < 1000000; ++i) {
        dummyVar = function() {
            var x = 0;
            x++;
        };
    }
}

function myFunc() {
    var x = 0;
    x++;
}

document.onclick = function() {
    var start = new Date();
    test1();
    var mid = new Date();
    test2();
    var end = new Date();
    alert ("Test 1: " + (mid - start) + "\n Test 2: " + (end - mid));
}

Hasil:
Tes 1: 142ms Tes 2: 1983ms

Tampaknya mesin JS tidak mengenali bahwa itu fungsi yang sama di Test2 dan mengkompilasinya setiap kali.

21
nickf

Sebagai prinsip desain umum, Anda harus menghindari pemberian kode yang sama beberapa kali. Alih-alih, Anda harus mengangkat kode umum ke fungsi dan menjalankan fungsi (umum, teruji dengan baik, mudah dimodifikasi) dari berbagai tempat.

Jika (tidak seperti apa yang Anda simpulkan dari pertanyaan Anda) Anda mendeklarasikan fungsi internal satu kali dan menggunakan kode itu satu kali (dan tidak memiliki hal lain yang identik dalam program Anda) maka fungsi anonomous mungkin (itu dugaan orang) diperlakukan cara yang sama oleh kompiler sebagai fungsi bernama normal.

Ini adalah fitur yang sangat berguna dalam hal tertentu, tetapi tidak boleh digunakan dalam banyak situasi.

2
Tom Leys

Di mana kita dapat memiliki dampak kinerja dalam operasi fungsi mendeklarasikan. Berikut ini adalah tolok ukur untuk mendeklarasikan fungsi di dalam konteks fungsi lain atau di luar:

http://jsperf.com/function-context-benchmark

Di Chrome operasinya lebih cepat jika kita mendeklarasikan fungsi di luar, tetapi di Firefox sebaliknya.

Dalam contoh lain kita melihat bahwa jika fungsi dalam bukan fungsi murni, itu akan memiliki kekurangan kinerja juga di Firefox: http://jsperf.com/function-context-benchmark-3

1
Pablo Estornut

Saya tidak akan berharap banyak perbedaan tetapi jika ada satu kemungkinan akan bervariasi oleh mesin scripting atau browser. 

Jika Anda menemukan kode lebih mudah untuk grok, kinerja bukan masalah kecuali Anda berharap untuk memanggil fungsi jutaan kali.

1
Joe Skora

Objek anonim lebih cepat dari objek bernama. Tetapi memanggil lebih banyak fungsi lebih mahal, dan pada tingkat yang melampaui penghematan yang mungkin Anda dapatkan dari menggunakan fungsi anonim. Setiap fungsi yang dipanggil menambah tumpukan panggilan, yang memperkenalkan jumlah overhead yang kecil namun tidak sepele.

Tetapi kecuali jika Anda menulis rutin enkripsi/dekripsi atau sesuatu yang sensitif terhadap kinerja, seperti banyak orang lain mencatat, selalu lebih baik untuk mengoptimalkan kode yang elegan dan mudah dibaca daripada kode cepat.

Dengan anggapan Anda sedang menulis kode yang dirancang dengan baik, maka masalah kecepatan harus menjadi tanggung jawab mereka yang menulis penterjemah/kompiler.

1
pcorcoran

@nickf

Itu adalah tes yang agak bodoh, Anda membandingkan eksekusi dan kompilasi kali ada yang jelas akan biaya metode 1 (mengkompilasi N kali, tergantung mesin JS) dengan metode 2 (kompilasi sekali). Saya tidak bisa membayangkan seorang pengembang JS yang akan lulus kode penulisan masa percobaan mereka sedemikian rupa.

Pendekatan yang jauh lebih realistis adalah penugasan anonim, karena sebenarnya Anda menggunakan untuk dokumen Anda. Metode klik lebih mirip sebagai berikut, yang sebenarnya lebih menyukai metode anon.

Menggunakan kerangka kerja pengujian yang serupa dengan milik Anda:


function test(m)
{
    for (var i = 0; i < 1000000; ++i) 
    {
        m();
    }
}

function named() {var x = 0; x++;}

var test1 = named;

var test2 = function() {var x = 0; x++;}

document.onclick = function() {
    var start = new Date();
    test(test1);
    var mid = new Date();
    test(test2);
    var end = new Date();
    alert ("Test 1: " + (mid - start) + "ms\n Test 2: " + (end - mid) + "ms");
}
0
annakata

@nickf

(berharap saya punya perwakilan hanya berkomentar, tapi saya baru saja menemukan situs ini)

Maksud saya adalah bahwa ada kebingungan di sini antara fungsi bernama/anonim dan use case mengeksekusi + kompilasi dalam iterasi. Seperti yang saya ilustrasikan, perbedaan antara anon + name dapat diabaikan dengan sendirinya - saya katakan itu adalah use case yang salah.

Tampaknya jelas bagi saya, tetapi jika tidak saya pikir saran terbaik adalah "jangan melakukan hal-hal bodoh" (di mana blok konstan bergeser + pembuatan objek kasus penggunaan ini adalah satu) dan jika Anda tidak yakin, uji!

0
annakata

IYA NIH! Fungsi anonim lebih cepat dari fungsi biasa. Mungkin jika kecepatan adalah yang paling penting ... lebih penting daripada penggunaan kembali kode maka pertimbangkan untuk menggunakan fungsi anonim.

Ada artikel yang sangat bagus tentang mengoptimalkan javascript dan fungsi anonim di sini:

http://dev.opera.com/articles/view/efisien-javascript/?page=2

0

referensi hampir selalu akan lebih lambat dari yang dirujuk. Pikirkan seperti ini - misalkan Anda ingin mencetak hasil penambahan 1 + 1. Yang lebih masuk akal:

alert(1 + 1);

atau

a = 1;
b = 1;
alert(a + b);

Saya menyadari itu cara yang sangat sederhana untuk melihatnya, tetapi itu ilustratif, bukan? Gunakan referensi hanya jika itu akan digunakan beberapa kali - misalnya, yang mana dari contoh-contoh ini lebih masuk akal:

$(a.button1).click(function(){alert('you clicked ' + this);});
$(a.button2).click(function(){alert('you clicked ' + this);});

atau

function buttonClickHandler(){alert('you clicked ' + this);}
$(a.button1).click(buttonClickHandler);
$(a.button2).click(buttonClickHandler);

Yang kedua adalah latihan yang lebih baik, bahkan jika ada lebih banyak baris. Semoga semua ini bermanfaat. (dan sintaks jquery tidak membuang siapa pun)

0
matt lohkamp

Seperti yang ditunjukkan dalam komentar untuk jawaban @nickf: Jawaban untuk 

Membuat fungsi lebih cepat sekali daripada membuatnya sejuta kali

hanya ya. Tapi seperti yang ditunjukkan perf JS-nya, itu tidak lebih lambat dengan faktor sejuta, menunjukkan bahwa itu sebenarnya semakin cepat dari waktu ke waktu.

Pertanyaan yang lebih menarik bagi saya adalah:

Bagaimana cara mengulang buat + jalankan bandingkan untuk membuat sekali + berulang jalankan .

Jika suatu fungsi melakukan perhitungan yang kompleks, waktu untuk membuat objek fungsi kemungkinan besar dapat diabaikan. Tapi bagaimana dengan kepala over create dalam kasus di mana run cepat? Contohnya:

// Variant 1: create once
function adder(a, b) {
  return a + b;
}
for (var i = 0; i < 100000; ++i) {
  var x = adder(412, 123);
}

// Variant 2: repeated creation via function statement
for (var i = 0; i < 100000; ++i) {
  function adder(a, b) {
    return a + b;
  }
  var x = adder(412, 123);
}

// Variant 3: repeated creation via function expression
for (var i = 0; i < 100000; ++i) {
  var x = (function(a, b) { return a + b; })(412, 123);
}

Ini JS Perf menunjukkan bahwa membuat fungsi hanya sekali lebih cepat seperti yang diharapkan. Namun, bahkan dengan operasi yang sangat cepat seperti penambahan sederhana, overhead untuk menciptakan fungsi berulang kali hanya beberapa persen.

Perbedaannya mungkin hanya menjadi signifikan dalam kasus-kasus di mana membuat objek fungsi itu kompleks, sambil mempertahankan waktu berjalan yang dapat diabaikan, misalnya, jika seluruh fungsi tubuh dibungkus menjadi if (unlikelyCondition) { ... }.

0
bluenote10

Yang pasti akan membuat perulangan Anda lebih cepat di berbagai peramban, terutama IE peramban, adalah perulangan sebagai berikut:

for (var i = 0, iLength = imgs.length; i < iLength; i++)
{
   // do something
}

Anda telah memasukkan 1000 arbitrer ke dalam kondisi loop, tetapi Anda mendapatkan maksud saya jika Anda ingin melihat semua item dalam array.

0
Sarhanis