このJavaScript / jQuery構文はどのように機能しますか:(function(window、undefined){})(window)?


153

jQuery 1.4ソースコードの内部を見て、それが次のようにカプセル化されていることに気づいたことがありますか。

(function( window, undefined ) {

  //All the JQuery code here 
  ...

})(window);

私はJavaScriptの名前空間に関する記事と「括弧の重要なペア」と呼ばれる別の記事を読んだので、ここで何が行われているのかを知っています。

しかし、この特定の構文を見たことはありません。そこで何をしundefinedているのですか?そして、なぜwindow渡されてから再び最後に現れる必要があるのですか?


3
私はポール・アイリッシュは、このビデオでは、このことについて話すことを追加したい:paulirish.com/2010/10-things-i-learned-from-the-jquery-source
Mottie

@Bergiは私の質問を別の質問の重複としてマークしましたが、重複のほぼ1年前に質問しました。キャストはその逆でなければなりません。
dkinzer 14

@dkinzer:それは、以前に尋ねられることではなく、最高品質の答えです。確かに、この場合、それは首と首の問題ですが、CMSの回答が最良のものであることがわかりました。来てchat.stackoverflow.com/rooms/17しかし、これを議論する
Bergi

回答:


161

undefinedは通常の変数であり、で簡単に変更できますundefined = "new value";。そのため、jQueryは、実際には未定義であるローカルの「未定義」変数を作成します。

ウィンドウ変数は、パフォーマンス上の理由からローカルになります。JavaScriptは変数を検索するとき、変数名が見つかるまで最初にローカル変数を調べます。JavaScriptが見つからない場合、JavaScriptはグローバル変数をフィルタリングするまで次のスコープなどを通過します。そのため、ウィンドウ変数をローカルにすると、JavaScriptですばやく検索できます。詳細情報:JavaScriptのスピードアップ-ニコラスC.ザカス


1
ありがとう!それは非常に有益なビデオでした。「ウィンドウ変数がローカルになっている」という応答を編集する必要があると思います。これが最良の答えだと思います。数えると、ウィンドウオブジェクトがJQueryソースコードで少なくとも17回呼び出されていることがわかりました。したがって、ウィンドウをローカルスコープに移動するには、大きな効果が必要です。
dkinzer、2010

@DKinzerああ、そうです、もちろんあなたはrghtです、もちろんそれはローカルです。私はあなたを助けることができてうれしい=)
Vincent

1
この更新されたテストによると、jsperf.com/short-scope/5がjwindowを介してウィンドウ定数を取得する場合、「ウィンドウ」を渡すのは実際には遅くなり、特にSafariには悪影響があります。したがって、「未定義」にはユースケースがありますが、「ウィンドウ」がすべての場合に特に有用であるとは確信がありません。
hexalys 2013

1
また、コードを縮小するためのプラスの効果もあります。したがって、「ウィンドウ」と「未定義」のすべての使用法は、ミニファイアによって短い名前に置き換えることができます。
TLindig 2013

2
@ lukas.pukenis何百万ものWebサイトで使用されるライブラリを作成するときは、狂人が何をするかについて想定しないのが賢明です。
JLRishe 2014

54

未定義

undefined引数として宣言するが値を渡さないことにより、上書きできるグローバルスコープ内の単なる変数であるため、常に未定義になります。これはa === undefinedtypeof a == 'undefined'数文字を節約するの安全な代替手段になります。また、コードをさらに縮小undefinedしてu、たとえば数文字を節約することもできます。

window引数として渡すと、コピーがローカルスコープに保持され、パフォーマンスに影響します:http : //jsperf.com/short-scope。へのすべてのアクセスwindowは、スコープチェーンの1レベル下を移動する必要があります。と同様にundefined、ローカルコピーでも、より積極的な縮小が可能です。


サイドノート:

これはjQuery開発者の意図ではなかったかもしれませんが、渡すwindowことで、グローバルオブジェクトがないnode.jsなどのサーバー側のJavaScript環境にライブラリをより簡単に統合できwindowます。このような状況では、windowオブジェクトを別の行に置き換えるために変更する必要があるのは1行だけです。jQueryの場合、windowHTMLスクレイピングの目的でモックオブジェクトを作成して渡すことができます(jsdomなどのライブラリがこれを行うことができます)。


2
、縮小化に言及ための+1は、私がjsperfのための2のことがしたい-縮小さバージョンがある(function(a,b){})(window);- ab比べて非常に短いwindowundefined:)
gnarf

+1 以前にスコープチェーンについて聞いたことがありますが、用語を忘れてしまいました。ありがとう!
アレックス、2011

同じ理由でvoid(0)を使用する代わりにも使用できます。void(0)は、未定義の値を取得する安全な方法です。
glmxndr

この他の質問に関連している「あなたは何と言う」:stackoverflow.com/questions/4945265/...
gnarf

@gnarf、質問が統合されたという通知をありがとう。私は私の答えをもう少し意味が
David Tang

16

他は説明したundefinedundefined任意の値に再定義できるグローバル変数のようなものです。この手法は、誰かがundefined = 10どこかで書いた場合にすべての未定義のチェックが壊れることを防ぐためのものです。渡されない引数undefinedは、変数の 値に関係なく実数であることが保証されていますundefined

ウィンドウを渡す理由は、次の例で説明できます。

(function() {
   console.log(window);
   ...
   ...
   ...
   var window = 10;
})();

コンソールは何をログに記録しますか?windowオブジェクトの価値は正しいですか?違う!10?違う!記録しundefinedます。JavaScriptインタープリター(またはJITコンパイラー)は、このように書き換えます-

(function() {
   var window; //and every other var in this function

   console.log(window);
   ...
   ...
   ...
   window = 10;

})();

ただし、window変数を引数として取得した場合、varはないため、驚きはありません。

jQueryが実行しているかどうかはわかりませんがwindow、何らかの理由で関数のローカル変数を再定義している場合は、グローバルスコープから借用することをお勧めします。


6

window誰かがIEでウィンドウオブジェクトを再定義することを決定した場合に備えて、そのように渡されますundefined。後で何らかの方法で再割り当てされた場合に備えて、についても同じと見なします。

このwindowスクリプトの先頭は、引数「ウィンドウ」に名前を付けているだけwindowです。これは、グローバル参照よりローカルで、このクロージャー内のコードが使用する引数です。window最後には、実際には最初の引数に渡すために何を指定して、このケースでの現在の意味window...望みはあなたが台無しにしていないでwindowそれが起こる前に。

これは、プラグイン、jQueryのに使用される最も典型的なケースを示すことによって考えるする方が簡単な場合があり.noConflict()ますので、まだ使用できるコードの大多数のため、取り扱いに$それが何かを意味していても、他のよりもjQuery、この範囲外に:

(function($) {
  //inside here, $ == jQuery, it was passed as the first argument
})(jQuery);

ありがとう。これは非常に理にかなっています。しかし、答えはこれと@ C.Zakasの発言の組み合わせだと思います。
dkinzer、2010

@DKinzer私はC. Zakasではないことを恐れています;)
Vincent

@Vincent、申し訳ありません:-)
dkinzer

5

1000000回の反復でテストされています。この種のローカリゼーションはパフォーマンスに影響を与えませんでした。1000000回の反復で1ミリ秒もありません。これは単に役に立たないだけです。


2
言うまでもなく、クロージャーは関数のインスタンスとともにすべての変数を運ぶため、クロージャーに100バイトがあり、1000インスタンスの場合は100kb以上のメモリが必要です。
Akash Kava 2013

@AkashKavaと@Semra、このテストは、実際の状況でウィンドウに合格する理由を実際に説明しているとは思いません。パフォーマンスの向上は、スコープチェーンに沿ってその変数にさらにアクセスする深くネストされたクロージャーがある場合にのみ見られます。関数がスコープチェーン上で変数から1ステップしか離れていない場合は、明らかに、あまり効果がありません。しかし、それがそこにあり、取得windowする必要がある場合は、ローカルコピーによる利益が見られる可能性があります。
gabereal 14

@gabereal新しいJavaScriptエンジンは、コンパイル時にクロージャを解決するため、実行時のクロージャのパフォーマンスは向上しません。測定可能なパフォーマンスの向上はないと思います。自信がある場合は、jsperfの例を作成してパフォーマンスを実証できます。私たちが得られる唯一のパフォーマンスは、クロージャーを分離することです。これにより、サイズが小さくなり、パフォーマンスが向上します。ダウンロードとスクリプトの解析が高速化されます。
Akash Kava 14

@AkashKava私自信があると思う理由は何ですか?私はちょうど「ある程度の利益が見られるかもしれない」と「私は思いません」と言った。テストを作成することはできましたが、とにかくこのパターンを使用したことがないので、作成したくありません。「コンパイル時にクロージャを解決する」とはどういう意味ですか?どのような選択肢とは対照的ですか?Javascriptエンジンは、以前はどのように異なる方法で動作しましたか?
gabereal 14

ウィンドウが最後にカリー化されているが、関数のパラメーターには入力されていないコードを見つけた場合、どういう意味ですか?それは、私が想定するウィンドウパラメータがない場合でも、パラメータが元のグローバルのスコープオブジェクトに従うことを保証しますか?
Webwoman
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.