Apakah penggunaan fungsi anonim memengaruhi kinerja?

89

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 jika Anda mendeklarasikan ulang fungsi itu beberapa kali?

nickf
sumber
Saya tahu ini bukan pertanyaannya, tetapi sehubungan dengan kebersihan / keterbacaan kode, saya pikir 'cara yang benar' ada di tengah-tengah. "Clutter" dari fungsi tingkat atas yang jarang digunakan memang mengganggu, tetapi begitu juga dengan kode bertingkat yang sangat bergantung pada fungsi anonim yang dideklarasikan sejalan dengan pemanggilannya (pikirkan callback node.js hell). Baik yang pertama maupun yang terakhir dapat mempersulit pelacakan debugging / eksekusi.
Zac B
Tes kinerja di bawah menjalankan fungsi untuk ribuan iterasi. Meskipun Anda melihat perbedaan yang substansial, sebagian besar kasus penggunaan tidak akan melakukan ini dalam pengulangan urutan tersebut. Karenanya, lebih baik memilih apa pun yang sesuai dengan kebutuhan Anda dan mengabaikan kinerja untuk kasus khusus ini.
pengguna
@nickf tentu saja pertanyaan yang terlalu lama, tapi lihat jawaban baru yang diperbarui
Chandan Pasunoori

Jawaban:

89

Masalah kinerja di sini adalah biaya pembuatan objek fungsi baru di setiap iterasi perulangan 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 meskipun objek tersebut memiliki badan kode yang sama dan tidak mengikat ke lingkup leksikal ( closure ). Sebaliknya, berikut ini tampak lebih cepat, karena ini hanya menetapkan referensi fungsi yang 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 memasuki loop, kemudian hanya menetapkan referensi ke elemen array sementara di dalam loop, Anda akan menemukan bahwa tidak ada perbedaan kinerja atau semantik apa pun jika dibandingkan dengan versi fungsi bernama:

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 bernama.

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

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

dan:

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

Yang pertama adalah deklarasi fungsi sedangkan yang terakhir adalah tugas variabel ke fungsi anonim. Meskipun mungkin tampak memiliki efek yang sama, JavaScript memperlakukannya sedikit berbeda. Untuk memahami perbedaannya, saya sarankan membaca, " ambiguitas deklarasi fungsi JavaScript ".

Waktu eksekusi aktual untuk pendekatan apa pun sebagian besar akan ditentukan oleh implementasi kompilator dan waktu proses browser. Untuk perbandingan lengkap kinerja browser modern, kunjungi situs JS Perf

Atif Aziz
sumber
Anda lupa tanda kurung sebelum badan fungsi. Saya baru saja mengujinya, mereka diwajibkan.
Chinoto Vokro
tampaknya hasil benchmark sangat bergantung pada mesin!
aleclofabbro
3
Apakah tidak ada kekurangan dalam contoh JS Perf: Kasus 1 hanya mendefinisikan fungsi, sedangkan kasus 2 & 3 sepertinya tidak sengaja memanggil fungsi tersebut.
bluenote10
Jadi dengan menggunakan alasan ini, apakah itu berarti bahwa saat mengembangkan node.jsaplikasi web, lebih baik membuat fungsi di luar aliran permintaan, dan meneruskannya sebagai callback, daripada membuat callback anonim?
Xavier T Mukodi
23

Ini kode tes 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 fungsinya sama di Test2 dan mengkompilasinya setiap kali.

nickf
sumber
3
Di browser manakah pengujian ini dilakukan?
andynil
5
Waktu untuk saya di Chrome 23: (2ms / 17ms), IE9: (20ms / 83ms), FF 17: (2ms / 96ms)
Davy8
Jawaban Anda layak mendapat bobot lebih. Waktu saya di Intel i5 4570S: Chrome 41 (1/9), IE11 (1/25), FF36 (1/14). Jelas bahwa fungsi anonim dalam sebuah loop berkinerja lebih buruk.
ThisClark
3
Tes ini tidak berguna seperti yang terlihat. Dalam kedua contoh tersebut, fungsi interior benar-benar dijalankan. Secara efektif, semua pengujian ini menunjukkan bahwa membuat fungsi 10.000.000 kali lebih cepat daripada membuat fungsi sekali.
Nucleon
2

Sebagai prinsip desain umum, Anda harus menghindari penerapan kode yang sama beberapa kali. Sebagai gantinya, Anda harus mengangkat kode umum menjadi sebuah fungsi dan menjalankan fungsi itu (umum, teruji dengan baik, mudah dimodifikasi) dari banyak tempat.

Jika (tidak seperti apa yang Anda simpulkan dari pertanyaan Anda) Anda mendeklarasikan fungsi internal sekali dan menggunakan kode itu sekali (dan tidak ada yang lain yang identik dalam program Anda) maka fungsi anonomous mungkin (itu tebakan orang) diperlakukan dengan cara yang sama oleh kompilator sebagai fungsi bernama normal.

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

Tom Leys
sumber
1

Saya tidak berharap banyak perbedaan tetapi jika ada, kemungkinan besar akan bervariasi menurut mesin skrip atau browser.

Jika Anda menemukan kode lebih mudah untuk digrok, kinerja tidak menjadi masalah kecuali Anda berharap untuk memanggil fungsi jutaan kali.

Joe Skora
sumber
1

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

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

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

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

Pablo Estornut
sumber
0

Apa yang pasti akan membuat loop Anda lebih cepat di berbagai browser, terutama browser IE, adalah sebagai berikut:

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

Anda telah memasukkan 1000 sewenang-wenang ke dalam kondisi loop, tetapi Anda mengerti maksud saya jika Anda ingin memeriksa semua item dalam array.

Sarhanis
sumber
0

referensi hampir selalu lebih lambat dari hal yang dirujuknya. Pikirkan seperti ini - katakanlah 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 ini ilustratif, bukan? Gunakan referensi hanya jika akan digunakan beberapa kali - misalnya, mana dari contoh berikut yang 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 barisnya lebih banyak. Semoga semua ini bermanfaat. (dan sintaks jquery tidak membuat siapa pun marah)

matt lohkamp
sumber
0

@tokopedia

(Seandainya saya punya perwakilan untuk hanya berkomentar, tetapi saya baru saja menemukan situs ini)

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

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

annakata
sumber
0

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

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

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

Christopher Tokar
sumber
0

Objek anonim lebih cepat daripada objek bernama. Tetapi memanggil lebih banyak fungsi lebih mahal, dan pada tingkat yang melebihi penghematan yang mungkin Anda peroleh dari penggunaan fungsi anonim. Setiap fungsi yang dipanggil menambah tumpukan panggilan, yang memperkenalkan jumlah overhead yang kecil tapi tidak sepele.

Tetapi kecuali Anda menulis rutinitas enkripsi / dekripsi atau sesuatu yang sensitif terhadap kinerja, seperti yang dicatat oleh banyak orang lain, selalu lebih baik untuk mengoptimalkan kode yang elegan dan mudah dibaca melalui kode cepat.

Dengan asumsi Anda menulis kode yang dirancang dengan baik, maka masalah kecepatan harus menjadi tanggung jawab mereka yang menulis interpreter / compiler.

pcorcoran.dll
sumber
0

@tokopedia

Itu adalah tes yang agak bodoh, Anda membandingkan waktu eksekusi dan kompilasi di sana yang jelas akan memakan biaya metode 1 (mengkompilasi N kali, tergantung mesin JS) dengan metode 2 (dikompilasi sekali). Saya tidak bisa membayangkan seorang pengembang JS yang akan melewati masa percobaan menulis kode dengan cara seperti itu.

Pendekatan yang jauh lebih realistis adalah penugasan anonim, karena sebenarnya Anda menggunakan metode document.onclick Anda lebih seperti berikut, yang pada kenyataannya sedikit lebih menyukai metode anon.

Menggunakan kerangka pengujian serupa dengan 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");
}
annakata
sumber
0

Seperti yang ditunjukkan di komentar untuk jawaban @nickf: Jawabannya

Membuat fungsi sekali lebih cepat daripada membuatnya jutaan kali

hanya ya. Tapi seperti yang ditunjukkan kinerja JS-nya, itu tidak lebih lambat dengan faktor satu juta, menunjukkan bahwa itu benar-benar menjadi lebih cepat dari waktu ke waktu.

Pertanyaan yang lebih menarik bagi saya adalah:

Bagaimana sebuah berulang membuat + run dibandingkan dengan membuat sekali + diulang menjalankan .

Jika suatu fungsi melakukan komputasi kompleks, waktu untuk membuat objek fungsi kemungkinan besar dapat diabaikan. Tapi bagaimana dengan over head of create jika lari cepat? Misalnya:

// 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 menciptakan fungsi hanya sekali lebih cepat seperti yang diharapkan. Namun, bahkan dengan operasi yang sangat cepat seperti penambahan sederhana, overhead pembuatan fungsi berulang kali hanya beberapa persen.

Perbedaan tersebut mungkin hanya menjadi signifikan dalam kasus di mana pembuatan objek fungsi itu rumit, dengan tetap mempertahankan waktu proses yang dapat diabaikan, misalnya, jika seluruh badan fungsi digabungkan ke dalam if (unlikelyCondition) { ... }.

bluenote10
sumber