Dapatkah saya menamai fungsi JavaScript dan menjalankannya dengan segera?

90

Saya punya beberapa di antaranya:

function addEventsAndStuff() {
  // bla bla
}
addEventsAndStuff();

function sendStuffToServer() {
  // send stuff
  // get HTML in response
  // replace DOM
  // add events:
  addEventsAndStuff();
}

Penambahan kembali peristiwa diperlukan karena DOM telah berubah, sehingga peristiwa yang dilampirkan sebelumnya akan hilang. Karena mereka harus dipasang pada awalnya juga (ya), mereka dalam fungsi yang bagus untuk menjadi KERING.

Tidak ada yang salah dengan pengaturan ini (atau apakah ada?), Tetapi bisakah saya menghaluskannya sedikit? Saya ingin membuat addEventsAndStuff()fungsi dan segera memanggilnya, jadi tidak terlihat begitu amatir.

Keduanya menanggapi berikut dengan kesalahan sintaks:

function addEventsAndStuff() {
  alert('oele');
}();

(function addEventsAndStuff() {
  alert('oele');
})();

Ada yang mau?

Rudie
sumber
kemungkinan duplikat dari fungsi eksekusi mandiri Javascript
Cristian Sanchez
2
Tidak, @CristianSanchez.
Máxima Alekz
Imho blok kode kedua Anda sulit dibaca. Di blok kode pertama, jelas bagi setiap pembaca, bahwa Anda memanggil fungsi setelah init. Pembaca cenderung mengabaikan tanda kurung tutup dan overread di bagian akhir.
kaiser

Jawaban:

123

Tidak ada yang salah dengan contoh yang Anda posting di pertanyaan Anda .. Cara lain melakukannya mungkin terlihat aneh, tetapi:

var addEventsAndStuff;
(addEventsAndStuff = function(){
    // add events, and ... stuff
})();

Ada dua cara untuk mendefinisikan fungsi di JavaScript. Deklarasi fungsi:

function foo(){ ... }

dan ekspresi fungsi, yang merupakan cara apa pun untuk mendefinisikan fungsi selain di atas:

var foo = function(){};
(function(){})();
var foo = {bar : function(){}};

... dll

ekspresi fungsi bisa diberi nama, tapi namanya tidak disebarkan ke lingkup yang mengandung. Artinya kode ini valid:

(function foo(){
   foo(); // recursion for some reason
}());

tapi ini bukan:

(function foo(){
    ...
}());
foo(); // foo does not exist

Jadi untuk menamai fungsi Anda dan segera memanggilnya, Anda perlu mendefinisikan variabel lokal, menetapkan fungsi Anda sebagai ekspresi, lalu memanggilnya.

Mark Kahn
sumber
1
"ekspresi fungsi dapat diberi nama" Waspadalah terhadap ekspresi fungsi bernama. Mereka harus bekerja seperti yang Anda gambarkan, tetapi tidak ada di IE sebelum IE9 - Anda mendapatkan dua fungsi, dan nama fungsi bocor. Detail: kangax.github.com/nfe (Ini akhirnya diperbaiki di IE9.)
TJ Crowder
@TJ - terima kasih atas referensinya. Saya tidak menyadarinya karena saya tidak pernah menggunakan IE kecuali untuk menguji produk akhir :) Saya berasumsi bahwa IE9 tidak meniru bug ini saat mengaturnya ke mode IE7 / 8? Saya rasa masih menggunakan mesin IE9 JS ... Meh
Mark Kahn
Jika, seperti saya, Anda mengalami masalah jshint dengan metode ini, Anda dapat menggunakan call()metode dari sini: stackoverflow.com/a/12359154/1231001
cfx
Pendekatan ini mungkin tampak berguna dalam keadaan tertentu, tetapi selain sedikit lebih sulit untuk dibaca, tampaknya memiliki jumlah teks yang hampir sama.
Vladimir
17

Ada cara singkat yang bagus untuk ini (tidak perlu mendeklarasikan variabel apa pun untuk menetapkan fungsi):

var func = (function f(a) { console.log(a); return f; })('Blammo')
James Gorrie
sumber
rFungsi mana yang akan dikembalikan? The function ratau var r? Hanya penasaran. Solusi bagus. Tidak harus menjadi satu lokasi =)
Rudie
Saya telah mengganti namanya menjadi lebih jelas, tetapi return rakan menjadi function rseperti itu ruang lingkupnya. Telah menulis Scala secara permanen mencoba untuk mendapatkan sesuatu secara online.
James Gorrie
@JamesGorrie, singkatan brilian, namun, saya mencoba dua kali di konsol, tampaknya func, func (), func () () semua deklarasi fungsi kembalian dari f (), tetapi bisakah kita menampilkan 'Blammo' oleh func () seperti yang diharapkan? :)?
mike652638
@ mike652638 Saya telah menjalankannya di konsol saya dan konsol log blammo?
James Gorrie
5

Tidak ada yang salah dengan pengaturan ini (atau apakah ada?), Tetapi bisakah saya menghaluskannya sedikit?

Lihat menggunakan delegasi acara sebagai gantinya. Di situlah Anda benar-benar menonton acara di wadah yang tidak hilang, dan kemudian menggunakan event.target(atau event.srcElementdi IE) untuk mencari tahu di mana peristiwa sebenarnya terjadi dan menanganinya dengan benar.

Dengan begitu, Anda hanya memasang penangan satu kali, dan penangan tetap berfungsi bahkan saat Anda menukar konten.

Berikut adalah contoh delegasi acara tanpa menggunakan lib helper:

(function() {
  var handlers = {};

  if (document.body.addEventListener) {
    document.body.addEventListener('click', handleBodyClick, false);
  }
  else if (document.body.attachEvent) {
    document.body.attachEvent('onclick', handleBodyClick);
  }
  else {
    document.body.onclick = handleBodyClick;
  }

  handlers.button1 = function() {
    display("Button One clicked");
    return false;
  };
  handlers.button2 = function() {
    display("Button Two clicked");
    return false;
  };
  handlers.outerDiv = function() {
    display("Outer div clicked");
    return false;
  };
  handlers.innerDiv1 = function() {
    display("Inner div 1 clicked, not cancelling event");
  };
  handlers.innerDiv2 = function() {
    display("Inner div 2 clicked, cancelling event");
    return false;
  };

  function handleBodyClick(event) {
    var target, handler;

    event = event || window.event;
    target = event.target || event.srcElement;

    while (target && target !== this) {
      if (target.id) {
        handler = handlers[target.id];
        if (handler) {
          if (handler.call(this, event) === false) {
            if (event.preventDefault) {
              event.preventDefault();
            }
            return false;
          }
        }
      }
      else if (target.tagName === "P") {
        display("You clicked the message '" + target.innerHTML + "'");
      }
      target = target.parentNode;
    }
  }

  function display(msg) {
    var p = document.createElement('p');
    p.innerHTML = msg;
    document.body.appendChild(p);
  }

})();

Contoh langsung

Perhatikan bagaimana jika Anda mengklik pesan yang secara dinamis ditambahkan ke halaman, klik Anda didaftarkan dan ditangani meskipun tidak ada kode untuk menghubungkan peristiwa pada paragraf baru yang ditambahkan. Perhatikan juga bagaimana penangan Anda hanyalah entri di peta, dan Anda memiliki satu penangan di atasnya document.bodyyang melakukan semua pengiriman. Sekarang, Anda mungkin mengakar ini pada sesuatu yang lebih bertarget daripada document.body, tetapi Anda mengerti. Selain itu, di atas pada dasarnya kami mengirim id, tetapi Anda dapat melakukan pencocokan sesulit atau sesederhana yang Anda suka.

Library JavaScript modern seperti jQuery , Prototype , YUI , Closure , atau beberapa lainnya harus menawarkan fitur delegasi acara untuk memperhalus perbedaan browser dan menangani kasus edge dengan rapi. jQuery tentu melakukannya, dengan its livedandelegate fungsinya, yang memungkinkan Anda menentukan penangan menggunakan berbagai selektor CSS3 (dan kemudian beberapa).

Misalnya, inilah kode yang setara menggunakan jQuery (kecuali saya yakin jQuery menangani kasus tepi versi mentah off-the-cuff di atas tidak):

(function($) {

  $("#button1").live('click', function() {
    display("Button One clicked");
    return false;
  });
  $("#button2").live('click', function() {
    display("Button Two clicked");
    return false;
  });
  $("#outerDiv").live('click', function() {
    display("Outer div clicked");
    return false;
  });
  $("#innerDiv1").live('click', function() {
    display("Inner div 1 clicked, not cancelling event");
  });
  $("#innerDiv2").live('click', function() {
    display("Inner div 2 clicked, cancelling event");
    return false;
  });
  $("p").live('click', function() {
    display("You clicked the message '" + this.innerHTML + "'");
  });

  function display(msg) {
    $("<p>").html(msg).appendTo(document.body);
  }

})(jQuery);

Salinan langsung

TJ Crowder
sumber
Saya tidak ingin menggunakan Event.target, karena itu bukan alwaus target yang tepat. Betul sekali. Jika Anda mengklik di IMGdalam a DIVdan DIVmemiliki acara, Event.targetakan menjadi IMGdan tidak ada cara untuk mendapatkan DIVobjek dengan baik. edit Btw Aku benci .live'acara' jQuery
Rudie
@ Rudie: Re event.target: Apa yang Anda gambarkan tidak salah, itu benar. Jika Anda mengklik bagian imgdalam a divdan Anda menonton div, event.targetis the img(and thisis the div). Coba lihat lagi di atas. Anda dapat memasang penangan di titik mana pun dalam rantai antara elemen yang diklik ( imgmisalnya) dan elemen yang Anda tonton (di atas, di bawah document.body). Re live: Saya sangat suka delegate, versi yang lebih berisi live. Saya menggunakan di liveatas karena itu paling dekat dengan apa yang telah saya lakukan dalam contoh mentah. Hormat kami,
TJ Crowder
4

Anda mungkin ingin membuat fungsi pembantu seperti ini:

function defineAndRun(name, func) {
    window[name] = func;
    func();
}

defineAndRun('addEventsAndStuff', function() {
    alert('oele');
});
pimvdb.dll
sumber
2
Mengapa ini solusi yang buruk? Karena fungsinya terdaftar di namespace global? Siapa yang menolak ini?
Rudie
Ide bagus
:)
4

Kode Anda mengandung kesalahan ketik:

(function addEventsAndStuff() {
  alert('oele');
)/*typo here, should be }*/)();

begitu

(function addEventsAndStuff() {
  alert('oele');
 })();

bekerja. Bersulang!

[ edit ] berdasarkan komentar: dan ini harus menjalankan dan mengembalikan fungsi sekaligus:

var addEventsAndStuff = (
 function(){
  var addeventsandstuff =  function(){
    alert('oele');
  };
  addeventsandstuff();
  return addeventsandstuff;
 }()
);
KooiInc
sumber
Tanda kurung bodoh semuanya terlihat sama !! Terima kasih! Bersulang!
Rudie
Ya ... Jadi eh ... Sebenarnya itu tidak berhasil. Fungsi dijalankan segera, ya, tetapi tidak terdaftar di mana pun, jadi memanggilnya lagi ->ReferenceError
Rudie
@ Rudie: baru saja mengetahui bahwa KomodoEdit akhirnya melakukan semua yang saya selalu ingin dilakukan oleh Aptana Studio (tetapi selalu rusak), dan di atas itu semua sangat dapat dikonfigurasi, cepat dan dikemas dengan add-on yang berguna. Dan itu menyoroti tanda kurung yang cocok: Anda bahkan dapat memberi tanda kurung yang cocok itu warna peringatan merah yang tegas! Cobalah, gratis: activestate.com/komodo-edit
KooiInc
Bung ... (Dan jelas saya mengetiknya di editor web SO, bukan di editor desktop.)
Rudie
Dan itu banyak pengetikan ekstra dan mengingat apa dan bagaimana cara mengetik. Saya menyukai solusi asli (tidak berfungsi), tetapi yang baru terlalu sulit =)
Rudie
2

Lebih sederhana lagi dengan ES6:

var result = ((a, b) => `${a} ${b}`)('Hello','World')
// result = "Hello World"
var result2 = (a => a*2)(5)
// result2 = 10
var result3 = (concat_two = (a, b) => `${a} ${b}`)('Hello','World')
// result3 = "Hello World"
concat_two("My name", "is Foo")
// "My name is Foo"
Alberto
sumber
Jadi bagaimana saya menyebutnya lagi? Apa namanya? Anda hanya menyimpan hasilnya, bukan fungsinya.
Rudie
Metode ini digunakan dalam beberapa kasus hanya jika Anda perlu memanggilnya segera dan Anda tidak ingin menjalankannya kembali. Bagaimanapun saya akan mengedit jawaban untuk menunjukkan bagaimana bisa memanggilnya lagi, bagaimana menurut Anda? @ Rudie
Alberto
1
Pertanyaan saya adalah tentang memberi nama dan menjalankannya kembali. concat_twobekerja dengan sempurna, TAPI selalu mendefinisikannya dalam lingkup global, jadi itu tidak akan berfungsi "use strict". Itu tidak penting bagi saya, tapi itu kerugian kecil.
Rudie
1

Jika Anda ingin membuat fungsi dan segera menjalankan -

// this will create as well as execute the function a()
(a=function a() {alert("test");})();

// this will execute the function a() i.e. alert("test")
a();
Praveen Lobo
sumber
1
Hati-hati, ketika Anda menggunakan fungsi bernama sebagai nilai tangan kanan (seperti yang Anda lakukan di atas), Anda menggunakan ekspresi fungsi bernama yang meskipun seharusnya valid, sayangnya memiliki masalah dalam berbagai implementasi (kebanyakan - quelle shock - IE ). Detail: kangax.github.com/nfe
TJ Crowder
0

Coba lakukan seperti itu:

var addEventsAndStuff = (function(){
    var func = function(){
        alert('ole!');
    };
    func();
    return func;
})();
Alexander Ruliov
sumber
1
Ini dekat, tetapi var foo = (function() {...})();memanggil fungsi sebelum tugas. Tanda kurung perlu menyertakan tugas. (Anda ingin menetapkan kemudian menelepon.)
David
Itu juga banyak mengetik DAN mengingat apa yang harus diketik. Maka saya lebih suka menggunakan panggilan saya saat ini.
Rudie