Bir JavaScript işlevini adlandırıp hemen çalıştırabilir miyim?


92

Bende bunlardan birkaçı var:

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

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

Olayların yeniden eklenmesi gereklidir, çünkü DOM değişmiştir, bu nedenle önceden eklenen olaylar gitmiş olur. Başlangıçta da takılmaları gerektiğinden (duh), KURU olmak için güzel bir işleve sahipler.

Bu kurulumda yanlış bir şey yok (veya var mı?), Ama biraz düzeltebilir miyim? addEventsAndStuff()Fonksiyonu oluşturmak ve hemen çağırmak istiyorum , bu yüzden çok amatörce görünmesin.

Aşağıdaki her ikisi de bir sözdizimi hatasıyla yanıt verir:

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

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

Alıcı var mı?



2
Hayır, @CristianSanchez.
Máxima Alekz

Imho, ikinci kod bloğunuzun okunması zor. İlk kod bloğunda, her okuyucu için init'ten sonra fonksiyonu çağırdığınız açıktır. Okuyucular, sondaki parantezleri kapatmayı ve fazla okumayı göz ardı etme eğilimindedir.
kaiser

Yanıtlar:


124

Sorunuzda gönderdiğiniz örnekte yanlış bir şey yok .. Bunu yapmanın diğer yolu tuhaf görünebilir, ancak:

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

JavaScript'te bir işlevi tanımlamanın iki yolu vardır. Bir işlev bildirimi:

function foo(){ ... }

ve yukarıdakilerden farklı bir işlevi tanımlamanın herhangi bir yolu olan bir işlev ifadesi :

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

...vb

işlev ifadeleri adlandırılabilir, ancak adları kapsama alanına yayılmaz. Bu kodun geçerli olduğu anlamına gelir:

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

ama bu değil:

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

Fonksiyonunuzu adlandırmak ve hemen çağırmak için yerel bir değişken tanımlamanız, fonksiyonunuzu ona ifade olarak atamanız ve sonra çağırmanız gerekir.


1
"işlev ifadeleri" adlandırılabilir Adlandırılmış işlev ifadelerine dikkat edin. Onlar gerektiğini açıkladığınız olarak çalışır ancak bunların IE9 öncesinde IE üzerinde yok - Alacağınız iki işlevleri ve işlev adı sızıntıları. Ayrıntılar: kangax.github.com/nfe (Bu nihayet IE9'da düzeltildi.)
TJ Crowder

@TJ - referans için teşekkürler. Nihai ürünleri test etmek dışında hiçbir zaman IE kullanmadığımdan farkında değildim :) IE9'un IE7 / 8 moduna ayarlarken bu hatayı taklit etmediğini varsayıyorum? Sanırım hala IE9 JS motorunu kullanıyor ... Meh
Mark Kahn

Benim gibi, bu yöntemle jshint sorunlarıyla karşılaşırsanız, yöntemi buradan kullanabilirsiniz call(): stackoverflow.com/a/12359154/1231001
cfx

Bu yaklaşım bazı durumlarda yararlı görünebilir, ancak okunması biraz daha zor olmasının yanı sıra, neredeyse aynı miktarda metne sahip gibi görünüyor.
Vladimir

17

Bunun için iyi bir kısaltma var (fonksiyonun atamasını herhangi bir değişkeni bar olarak bildirmeye gerek yoktur):

var func = (function f(a) { console.log(a); return f; })('Blammo')

rİşlev hangisini döndürür? function rYa var r? Sadece merak. Güzel çözüm. Yine de tek bir yer olmak zorunda değildi =)
Rudie

Daha net olması için yeniden adlandırdım, ancak kapsamı olduğu gibi return rolurdu function r. Scala'yı o kadar kalıcı olarak yazıp oneline'den bir şeyler almaya çalışıyorum.
James Gorrie

@JamesGorrie, Brilliant stenografi, ancak, konsolda iki kez denedim, func, func (), func () () gibi görünüyor, hepsi f () işlev bildirimlerinin tümü, ancak beklendiği gibi 'Blammo'yu func () ile çıkarabilir miyiz lütfen :)?
mike652638

@ mike652638 Bunu konsolumda çalıştırdım ve konsol blammo günlüğünü tutuyor?
James Gorrie

5

Bu kurulumda yanlış bir şey yok (veya var mı?), Ama biraz düzeltebilir miyim?

Bunun yerine olay yetkilendirmeyi kullanmaya bakın. Orası , gitmeyen bir kapta olayı izlediğiniz yer ve ardından olayın gerçekte nerede meydana geldiğini bulmak ve doğru şekilde ele almak için event.target(veya event.srcElementIE'de) kullanın.

Bu şekilde, işleyicileri yalnızca bir kez eklersiniz ve içerik değiştirdiğinizde bile çalışmaya devam ederler.

Herhangi bir yardımcı kitap kullanmadan olay yetkilendirmesine bir örnek:

(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);
  }

})();

Canlı örnek

Sayfaya dinamik olarak eklenen mesajları tıklarsanız, eklenen yeni paragraflarda olayları kancalamak için bir kod olmasa bile tıklamanızın nasıl kaydedileceğini ve işleneceğini unutmayın. Ayrıca işleyicilerinizin bir haritadaki girişler olduğunu ve tüm dağıtımı yapan bir işleyicinizin document.bodyolduğunu da unutmayın. Şimdi, muhtemelen bunu daha hedefli bir şeye dayandırıyorsunuz document.body, ama fikri anladınız. Ayrıca, yukarıda temelde gönderiyoruz id, ancak istediğiniz kadar karmaşık veya basit eşleştirme yapabilirsiniz.

JQuery , Prototype , YUI , Closure veya diğer birkaç gibi modern JavaScript kitaplıkları , tarayıcı farklılıklarını düzeltmek ve uç durumları temiz bir şekilde ele almak için olay yetkilendirme özellikleri sunmalıdır. jQuery kesinlikle hem ile, yapar liveve delegatesize (ve daha sonra) CSS3 seçicileri bir dizi kullanarak yakalayabilmek izin işlevleri.

Örneğin, jQuery kullanan eşdeğer kod burada (ancak jQuery'nin yukarıda belirtilen ham sürümün uç durumları ele aldığından eminim):

(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);

Canlı kopya


Event.targetKullanmak istemiyorum çünkü bu her zaman doğru hedef değil. Doğru. Bir IMGiçerideki a'ya tıklarsanız DIVve DIVolay varsa, Event.targeto olacaktır IMGve DIVnesneyi güzelce elde etmenin bir yolu yoktur . düzenle Btw .live
jQuery'nin

@Rudie: Re event.target: Tarif ettiğiniz şey yanlış değil, doğru. Bir tıklarsanız imgiç a divve izlediğiniz div, event.targetolduğu img(ve thisolduğu div). Yukarıya bir kez daha bakın. Zincirin herhangi bir noktasına tıklanan öğe ( imgörneğin) ile izlediğiniz öğe (yukarıda, sonuna kadar) arasına bir işleyici ekleyebilirsiniz document.body. Re live: Ben büyük ölçüde delegate, daha kapsayıcı sürümünü tercih ederim live. liveYukarıda kullandım çünkü ham örnekte yaptığıma en yakın olanıydı. En iyisi,
TJ Crowder

4

Bunun gibi bir yardımcı işlev oluşturmak isteyebilirsiniz:

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

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

2
Bu neden kötü bir çözüm? İşlevler genel ad alanında kayıtlı olduğu için mi? Buna kim oy verdi?
Rudie

Harika fikir
:)

4

Kodunuz bir yazım hatası içeriyor:

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

yani

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

İşler. Şerefe!

[ düzenle ] yoruma göre: ve bu, tek seferde çalışmalı ve işlevi döndürmelidir:

var addEventsAndStuff = (
 function(){
  var addeventsandstuff =  function(){
    alert('oele');
  };
  addeventsandstuff();
  return addeventsandstuff;
 }()
);

Aptal parantezlerin hepsi birbirine benziyor !! Teşekkürler! Gerçekten şerefe!
Rudie

Evet ... Yani eh ... Aslında bu işe yaramıyor. İşlev hemen yürütülür, evet, ancak hiçbir yerde kayıtlı değil, bu yüzden tekrar çağırmak ->ReferenceError
Rudie

@Rudie: KomodoEdit'in nihayet Aptana Studio'nun her zaman yapmasını istediğim her şeyi yaptığını (ancak her zaman kırıldığını) ve ayrıca son derece yapılandırılabilir, hızlı ve kullanışlı eklentilerle dolu olduğunu keşfettim. Ve eşleşen parantezleri vurgular: Bu eşleşen parantezlere sabit kırmızı bir uyarı rengi bile verebilirsiniz! :, Bedava Bir deneyin activestate.com/komodo-edit
KooiInc

Dostum ... (Ve tabii ki ben bir masaüstü editörü, SO web editörü yazmış olmanızdır.)
Rudie

Ve bu, neyi ve nasıl yazılacağını hatırlamak için çok fazla ekstra yazma ve hatırlama. Orijinal (çalışmayan) çözümü beğendim, ancak yenisi çok zor =)
Rudie

2

ES6 ile daha da basit:

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"

Öyleyse nasıl yeniden adlandırırım? Adı ne? Yalnızca sonucu tutuyorsunuz, işlevi değil.
Rudie

Bu yöntem, bazı durumlarda yalnızca hemen aramanız gerektiğinde ve yeniden çalıştırmak istemiyorsanız kullanılır. Her neyse, cevabı nasıl tekrar arayabileceğimi göstermek için düzenleyeceğim, ne düşünüyorsun? @Rudie
Alberto

1
Sorum, onu adlandırmak ve yeniden çalıştırmakla ilgiliydi. concat_twomükemmel çalışır, ANCAK onu her zaman küresel kapsamda tanımlar, bu nedenle birlikte çalışmaz "use strict". Bu benim için önemli değil ama küçük bir dezavantaj.
Rudie

1

Bir işlev oluşturmak ve hemen yürütmek istiyorsanız -

// 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();

1
Dikkat edin, sağ el değeri olarak adlandırılmış bir işlevi kullandığınızda (yukarıda yaptığınız gibi), geçerli olması gerekirken, maalesef çeşitli uygulamalarda sorunları olan adlandırılmış bir işlev ifadesi kullanıyorsunuzdur (çoğunlukla - quelle shock - IE ). Ayrıntılar: kangax.github.com/nfe
TJ Crowder

0

Böyle yapmaya çalışın:

var addEventsAndStuff = (function(){
    var func = function(){
        alert('ole!');
    };
    func();
    return func;
})();

1
Bu yakındır, ancak var foo = (function() {...})();atamadan önce işlevi çağırır. Parantez, ödevi içermelidir. (Atamak ve sonra aramak istiyorsunuz .)
David

Bu aynı zamanda çok fazla yazmak VE ne yazacağını hatırlamaktır. O zaman mevcut aramalarımı kullanmayı tercih ederim.
Rudie
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.