JavaScript関数に名前を付けてすぐに実行できますか?


89

私はこれらのかなりの数を持っています:

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

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

DOMが変更されたため、イベントを再度追加する必要があり、以前に添付されていたイベントはなくなります。それらは最初にも取り付けられなければならないので(duh)、それらはDRYであるために素晴らしい機能にあります。

この設定に問題はありません(またはありますか?)が、少しスムーズにできますか?addEventsAndStuff()関数を作成してすぐに呼び出したいので、それほど素人っぽくはありません。

次の両方が構文エラーで応答します。

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

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

テイカーはいますか?



2
いいえ、@ CristianSanchez。
マキシマAlekz

2番目のコードブロックは読みにくいです。最初のコードブロックでは、すべてのリーダーにとって、initの後に関数を呼び出すことは明らかです。読者は、最後の閉じ括弧と読み過ぎ括弧を無視する傾向があります。
カイザー2018年

回答:


123

質問に投稿した例に問題はありません。他の方法は奇妙に見えるかもしれませんが、次のようになります。

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

JavaScriptで関数を定義する方法は2つあります。関数宣言:

function foo(){ ... }

であり、関数式、任意の上記以外の機能を定義する方法:

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

...等

関数式には名前を付けることができますが、その名前は含まれているスコープに伝達されません。このコードが有効であることを意味します:

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

しかし、これはそうではありません:

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

したがって、関数に名前を付けてすぐに呼び出すには、ローカル変数を定義し、関数を式として割り当ててから呼び出す必要があります。


1
「関数式には名前を付けることができます」名前付き関数式に注意してください。彼らはすべきであるあなたが説明するように動作しますが、彼らはIE9より前のIEにしないでください-あなたが得る2つの機能、および関数名リークを。詳細:kangax.github.com/nfe(これはIE9で最終的に修正されました。)
TJ Crowder

@ TJ-参照してくれてありがとう。最終製品をテストする以外はIEを使用しないので、気づいていませんでした:) IE9をIE7 / 8モードに設定すると、このバグをエミュレートしないと思いますか?まだIE9JSエンジンを使用していると思います... Meh
Mark Kahn

私のように、このメソッドでjshintの問題が発生した場合は、次のcall()メソッドを使用できます:stackoverflow.com/a/12359154/1231001
cfx

このアプローチは特定の状況では役立つように思われるかもしれませんが、少し読みにくいだけでなく、実質的に同じ量のテキストがあるようです。
ウラジミール

15

これには良い速記があります(関数の割り当てを禁止する変数を宣言する必要はありません):

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

r関数はどちらを返しますか?function rまたはvar r?ちょっと興味があるんだけど。良い解決策。ただし、1つの場所である必要はありませんでした=)
Rudie 2014年

私はそれがより明確にするために名前を変更しましたが、return rあったであろうfunction rというのは、それの範囲でした。Scalaを書いているので、永久にonelineで何かを取得しようとしています。
ジェームズ・ゴリー2014年

@ JamesGorrie、素晴らしい速記ですが、コンソールで2回試しました。すべて、func、func()、func()()がf()の関数宣言を返すようですが、期待どおりにfunc()で「Blammo」を出力できますか? :)?
mike6526 3819

@ mike652638コンソールで実行しましたが、コンソールはblammoをログに記録しますか?
ジェームズ・ゴリー

5

この設定に問題はありません(またはありますか?)が、少しスムーズにできますか?

代わりに、イベント委任の使用を検討してください。ここで、消えないコンテナーでイベントを実際に監視し、event.target(またはevent.srcElementIEで)使用して、イベントが実際に発生した場所を特定し、正しく処理します。

そうすれば、ハンドラーをアタッチするのは1回だけで、コンテンツを交換してもハンドラーは機能し続けます。

ヘルパーライブラリを使用しないイベント委任の例を次に示します。

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

})();

実例

ページに動的に追加されるメッセージをクリックすると、追加される新しい段落にイベントをフックするコードがない場合でも、クリックが登録されて処理されることに注意してください。また、ハンドラーがマップ内の単なるエントリであり、すべてのディスパッチを実行するハンドラーが1つあることにも注意してくださいdocument.body。さて、あなたはおそらくこれをよりターゲットを絞った何かに根ざしているでしょうdocument.body、しかしあなたはその考えを理解します。また、上記では基本的にはでディスパッチしてidいますが、複雑なものから単純なものまで、好きなだけマッチングを行うことができます。

jQueryPrototypeYUIClosure、またはその他のいくつかのような最新のJavaScriptライブラリは、ブラウザの違いをスムーズにし、エッジケースをきれいに処理するためのイベント委任機能を提供する必要があります。jQueryは、その関数livedelegate関数の両方で確かに機能します。これにより、CSS3セレクターの全範囲(および一部)を使用してハンドラーを指定できます。

たとえば、jQueryを使用した同等のコードは次のとおりです(jQueryがエッジケースを処理すると確信している場合を除きます)。

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

ライブコピー


私は使いたくありませんEvent.target。なぜなら、それは必ずしも正しいターゲットではないからです。そのとおり。あなたはをクリックするとIMG内部のADIVDIVイベントがあり、Event.targetなりますIMGと、取得する方法はありませんDIVうまくオブジェクトが。編集ところで私はjQueryの.live「イベント」が嫌いです
Rudie 2011年

@Rudie:Re event.target:あなたが説明することは間違っていません、それは正しいです。あなたがクリックした場合imgの内部Aをdiv、あなたが監視していますdivevent.targetであるimg(とthisありますdiv)。上記をもう一度見てください。クリックされた要素(imgたとえば、)と監視している要素(上記では、ずっと下document.body)の間のチェーン内の任意のポイントにハンドラーをアタッチできます。Re live:私は非常に好きdelegateです、より含まれているバージョンのlivelive生の例で行ったことに最も近いので、上記を使用しました。ベスト、
TJクラウダー

4

次のようなヘルパー関数を作成することをお勧めします。

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

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

2
なぜこれが悪い解決策なのですか?関数はグローバル名前空間に登録されているので?誰がこれに投票しましたか?
ルディ2011年

素晴らしいアイデア:))
マキシマAlekz

4

コードにタイプミスが含まれています:

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

そう

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

動作します。乾杯!

コメントに基づいて[編集]:これは実行され、関数を一度に返す必要があります:

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

愚かなブラケットはすべて同じように見えます!! ありがとう!本当に乾杯!
ルディ2011年

ええ...だからええ...実際にはそれはうまくいきません。関数はすぐに実行されます、はい、しかしどこにも登録されていないので、もう一度呼び出します->ReferenceError
Rudie 2011年

@Rudie:KomodoEditが、私がいつもAptana Studioに実行してほしいことをすべて実行することを発見しました(ただし、常に壊れています)。さらに、高度に構成可能で、高速で、便利なアドオンが満載です。そして、それは一致するブラケットを強調します:あなたはそれらの一致するブラケットにしっかりした赤い警告色を与えることさえできます!試してみてください。無料です:activestate.com/komodo-edit
KooiInc

おい...(そして明らかに私はデスクトップエディタではなく、SO Webエディタで入力しました。)
Rudie

そして、それは多くの余分なタイピングであり、何をどのようにタイプするかを覚えています。元の(機能していない)ソリューションが好きでしたが、新しいソリューションは難しすぎます=)
Rudie 2011年

2

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"

それで、私はそれを再びどのように呼ぶでしょうか?それを呼ぶために、その名前は何ですか?関数ではなく、結果のみを保持します。
ルディ

このメソッドは、すぐに呼び出す必要があり、再実行したくない場合にのみ使用される場合があります。とにかく、私は答えを編集して、それを再び呼び出すことができる方法を示します、あなたはどう思いますか?@Rudie
アルベルト

1
私の質問は、名前を付けて再実行することについてでした。concat_two完全に機能しますが、常にグローバルスコープで定義されるため、では機能しません"use strict"。それは私にとって重要ではありませんが、それは小さな欠点です。
ルディ

1

関数を作成してすぐに実行したい場合-

// 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
名前付き関数を右側の値として使用する場合(上記のように)、名前付き関数式を使用していることに注意してください。名前付き関数式有効であるはずですが、残念ながらさまざまな実装で問題が発生します(ほとんどの場合-quelle Shock-IE )。詳細:kangax.github.com/nfe
TJ Crowder

0

そのようにしてみてください:

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

1
これは近いですがvar foo = (function() {...})();、割り当ての前に関数を呼び出します。括弧には割り当てを含める必要があります。(割り当ててから呼び出します。)
David

それはまた多くのタイピングであり、何をタイプするかを覚えています。次に、現在の通話を使用したいと思います。
ルディ2011年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.