「(function(){…})()」のような匿名関数でJavascriptファイル全体をラップする目的は何ですか?


584

私は最近多くのJavascriptを読んでいて、インポートされる.jsファイルでファイル全体が次のようにラップされていることに気づきました。

(function() {
    ... 
    code
    ...
})();

コンストラクター関数の単純なセットではなく、これを行う理由は何ですか?


6
これは多くの人に使われると思いますので、閉会をお忘れなく。
2013年

5
この技術は「IIFE」と呼ばれていると思います。これは、即時に呼び出される関数式en.wikipedia.org/wiki/Immediately-invoked_function_expressionの
Adrien Be

回答:


786

これは通常、名前空間(後述)に割り当てられ、メンバー関数や変数の表示を制御します。オブジェクト定義のように考えてください。その技術名は、Immediately Invoked Function Expression(IIFE)です。jQueryプラグインは通常、次のように記述されます。

JavaScriptでは、関数をネストできます。したがって、以下は合法です。

function outerFunction() {
   function innerFunction() {
      // code
   }
}

これでを呼び出すことができますouterFunction()が、の可視性innerFunction()はのスコープに制限されています。outerFunction()つまり、はプライベートouterFunction()です。基本的には、JavaScriptの変数と同じ原則に従います。

var globalVariable;

function someFunction() {
   var localVariable;
}

対応して:

function globalFunction() {

   var localFunction1 = function() {
       //I'm anonymous! But localFunction1 is a reference to me!
   };

   function localFunction2() {
      //I'm named!
   }
}

上記のシナリオでは、globalFunction()どこからでも呼び出すことができますが、localFunction1またはを呼び出すことはできませんlocalFunction2

あなたが書いているときあなた(function() { ... })()がしていることは、あなたは括弧の最初のセットの中のコードを関数リテラルにすることです(つまり、「オブジェクト」全体が実際には関数であることを意味します)。その後、()先ほど定義した関数(最後の)を自己起動します。先に述べたように、これの主な利点は、プライベートメソッド/関数とプロパティを持つことができることです:

(function() {
   var private_var;

   function private_function() {
     //code
   }
})();

最初の例では、globalFunction名前で明示的に呼び出して実行します。つまり、実行するだけですglobalFunction()。しかし、上記の例では、関数を定義するだけではありません。あなたはそれを一度に定義して呼び出すことになります。これは、JavaScriptファイルがロードされるとすぐに実行されることを意味します。もちろん、次のことができます。

function globalFunction() {
    // code
}
globalFunction();

動作は、1つの重要な違いを除いてほとんど同じです。IIFEを使用すると、グローバルスコープの汚染を回避できます(その結果、関数には名前がないため、関数を複数回呼び出すことはできません。この関数は、実際には問題にならない場合にのみ実行することを目的としています)。

IIFEの優れた点は、内部で物事を定義し、必要な部分のみを外部に公開できることです(ネームスペースの例なので、基本的に独自のライブラリ/プラグインを作成できます)。

var myPlugin = (function() {
 var private_var;

 function private_function() {
 }

 return {
    public_function1: function() {
    },
    public_function2: function() {
    }
 }
})()

これで電話をかけることができmyPlugin.public_function1()ますが、アクセスできませんprivate_function()!クラスの定義と非常によく似ています。これをよりよく理解するために、以下のリンクをお読みになることをお勧めします。

編集

私は言及するのを忘れていました。そのファイナルでは()、あなたが欲しいものは何でも渡すことができます。あなたはjQueryのプラグインを作成する場合たとえば、あなたが渡すjQueryか、$そうのように:

(function(jQ) { ... code ... })(jQuery) 

したがって、ここで行っているのは、1つのパラメーターを受け取る関数(jQローカル変数と呼ばれ、その関数だけが知っている)を定義することです。次に、関数を自己呼び出ししてパラメーターを渡します(とも呼ばれますjQueryが、これは外部からのものであり、実際のjQuery自体への参照です)。これを行う必要はありませんが、いくつかの利点があります。

  • グローバルパラメータを再定義して、ローカルスコープで意味のある名前を付けることができます。
  • スコープチェーンをグローバルスコープに移動するよりも、ローカルスコープで検索する方が高速であるため、パフォーマンスがわずかに向上します。
  • 圧縮(縮小)には利点があります。

前に、これらの関数が起動時にどのように自動的に実行されるかを説明しましたが、それらが自動的に実行される場合、だれが引数を渡していますか?この手法では、必要なすべてのパラメーターがすでにグローバル変数として定義されていることを前提としています。したがって、jQueryがまだグローバル変数として定義されていない場合、この例は機能しません。ご想像のとおり、jquery.jsが初期化中に行うことの1つは、「jQuery」グローバル変数と、より有名な「$」グローバル変数を定義することです。


14
とてもクールです。名前空間はよく理解していますが、最後の例をたくさん見ましたが、人々が何を達成しようとしているか理解できませんでした。これは本当に事をきれいにします。
アンドリューコウ

34
素晴らしい投稿。どうもありがとう。
Darren

4
先頭と末尾にセミコロン「;」を追加すると思います。;(function(jQ) { ... code ... })(jQuery);この例では、スクリプトをセミコロンで省略しても、特にスクリプトを縮小して他のスクリプトと連結しようとする場合は、セミコロンを省略しても問題はありません
Taras Alenin 2014

3
いいポスト、私はプライベート変数に重点を置くのが好きです。また、モジュールパターン/クロージャー(public_function1&public_function2)の冒頭部分と、変数の受け渡し方法も気に入っています。ただし、多少範囲外になったとしても、これはすばらしい導入です。また、答えを追加しました。これは、構文の根本と関数ステートメントと関数式の違い、および「単なる規則」と「この結果を達成するための唯一の方法」だと私が考えるものに焦点を当てています。
エイドリアンBe 14

4
素晴らしい投稿です。変数を自己実行関数に渡すことがどのように有益であるかについては、おそらくもっと詳しく思います。自己実行関数のコンテキストはクリーンです-データがありません。これ(function (context) { ..... })(this)を行うことにより、コンテキストを渡すことができます。これにより、親コンテキストに好きなものをアタッチして、それを公開できます。
Callum Linington 2014年

79

要するに

概要

最も簡単な形式では、この手法はコードを関数スコープ内にラップすることを目的としています

以下の可能性を減らすのに役立ちます。

  • 他のアプリケーション/ライブラリとの衝突
  • 優れた(世界で最も可能性が高い)範囲を汚染する

それはしない文書の準備ができたときを検出-それはいくつかの種類ではありませんdocument.onloadwindow.onload

通常、Immediately Invoked Function Expression (IIFE)またはとして知られていSelf Executing Anonymous Functionます。

コードの説明

var someFunction = function(){ console.log('wagwan!'); };

(function() {                   /* function scope starts here */
  console.log('start of IIFE');

  var myNumber = 4;             /* number variable declaration */
  var myFunction = function(){  /* function variable declaration */
    console.log('formidable!'); 
  };
  var myObject = {              /* object variable declaration */
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
})();                           /* function scope ends */

someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

上記の例では、関数で定義された(つまりを使用して宣言された)変数はすべてvar「プライベート」になり、関数スコープ内でのみアクセスできます(Vivin Paliathによると)。つまり、これらの変数は、関数の外からは見えず、到達することもできません。ライブデモをご覧ください

Javascriptには関数スコープがあります。「関数で定義されたパラメータと変数は関数の外からは見えません。また、関数内のどこかで定義された変数は関数内のどこからでも見えます。」(「Javascript:良い部分」から)。


もっと詳しく

代替コード

最後に、前に投稿されたコードは次のように実行することもできます。

var someFunction = function(){ console.log('wagwan!'); };

var myMainFunction = function() {
  console.log('start of IIFE');

  var myNumber = 4;
  var myFunction = function(){ console.log('formidable!'); };
  var myObject = { 
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
};

myMainFunction();          // I CALL "myMainFunction" FUNCTION HERE
someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

ライブデモをご覧ください


ルーツ

反復1

ある日、誰かが「すぐに実行するだけでよいので、 'myMainFunction'に名前を付けないようにする方法があるはずだ」と考えた可能性があります。

基本に戻ると、次のことがわかります。

  • expression:値に評価されるもの。すなわち3+11/x
  • statement:何かを実行するコード行ですが、値に評価されませ。すなわちif(){}

同様に、関数式は値に評価されます。そして、1つの結果(私はそう思いますか?)はすぐに呼び出せるということです。

 var italianSayinSomething = function(){ console.log('mamamia!'); }();

したがって、より複雑な例は次のようになります。

var someFunction = function(){ console.log('wagwan!'); };

var myMainFunction = function() {
  console.log('start of IIFE');

  var myNumber = 4;
  var myFunction = function(){ console.log('formidable!'); };
  var myObject = { 
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
}();

someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

ライブデモをご覧ください

反復2

次のステップは、「var myMainFunction =私たちがそれを使用しないのならなぜあるのか!?」という考えです。

答えは簡単です。以下のように削除してみてください。

 function(){ console.log('mamamia!'); }();

ライブデモをご覧ください

「関数宣言は起動できない」ため、機能しません

トリックは、削除するvar myMainFunction =ことで関数式関数宣言に変換したことです。詳細については、「リソース」のリンクを参照してください。

次の質問は、「なぜそれを関数式として保持できないのvar myMainFunction =ですか?

答えは「あなたができる」で、あなたがこれを行うことができます多くの方法実際にあります追加+!-、または多分(それは今大会で行うのと)括弧のペアで包み、そしてより多くの私は信じては。例として:

 (function(){ console.log('mamamia!'); })(); // live demo: jsbin.com/zokuwodoco/1/edit?js,console.

または

 +function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wuwipiyazi/1/edit?js,console

または

 -function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wejupaheva/1/edit?js,console

したがって、「代替コード」であったものに関連する変更が追加されると、「コードの説明」の例で使用されたものとまったく同じコードに戻ります。

var someFunction = function(){ console.log('wagwan!'); };

(function() {
  console.log('start of IIFE');

  var myNumber = 4;
  var myFunction = function(){ console.log('formidable!'); };
  var myObject = { 
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
})();

someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

についてもっと読むExpressions vs Statements


スコープの謎を解く

「関数内で変数を「適切に」定義しないとどうなりますか?代わりに単純な代入を行いますか?」

(function() {
  var myNumber = 4;             /* number variable declaration */
  var myFunction = function(){  /* function variable declaration */
    console.log('formidable!'); 
  };
  var myObject = {              /* object variable declaration */
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  myOtherFunction = function(){  /* oops, an assignment instead of a declaration */
    console.log('haha. got ya!');
  };
})();
myOtherFunction();         // reachable, hence works: see in the console
window.myOtherFunction();  // works in the browser, myOtherFunction is then in the global scope
myFunction();              // unreachable, will throw an error, see in the console

ライブデモをご覧ください

基本的に、現在のスコープで宣言されていない変数に値が割り当てられている場合、「変数が見つかるか、グローバルスコープに到達するまで(スコープが作成される)、スコープチェーンのルックアップが行われます」。

ブラウザー環境(nodejsのようなサーバー環境に対して)の場合、グローバルスコープはwindowオブジェクトによって定義されます。したがって、私たちは行うことができますwindow.myOtherFunction()

このトピックに関する私の「グッドプラクティス」のヒントは、何かを定義するとき常に使用varすることですです。それが数値、オブジェクト、または関数であるかどうか、さらにはグローバルスコープ内であってもです。これにより、コードがはるかに簡単になります。

注意:

  • javascriptにはありませblock scope(更新:ES6で追加されたブロックスコープのローカル変数た)。
  • javascriptにはfunction scopeglobal scopewindowブラウザ環境ではスコープ)しかない

についてもっと読むJavascript Scopes


資源


次のステップ

このIIFE概念をmodule pattern理解したら、それはにつながります。これは、通常、このIIFEパターンを利用することによって行われます。楽しんで :)


非常に役立ちます。どうもありがとう!
Christoffer Helgelin Hald

ニース、私はデモ版を好む:)
Fabrizio Bertoglio

そのような素晴らしい説明。ありがとうございました!
Vikram Khemlani

26

ブラウザのJavaScriptには、実際には関数スコープとグローバルスコープという2つの有効なスコープしかありません。

変数が関数スコープにない場合、グローバルスコープにあります。そして、グローバル変数は一般的に悪いので、これはライブラリの変数をそれ自体に保つための構造です。


1
しかし、コンストラクター関数自体が独自の変数のスコープを提供していませんか?
Andrew Kou

1
はい、このライブラリで定義された各関数は独自のローカル変数を定義できますが、これにより、変数がライブラリの外部にリークすることなく関数間で共有できます
Gareth

@Gareth。これにより、スコープ内で「グローバル」変数を使用できます(;
Francisco Presencia、2015年

2
@FranciscoPresencia「スコープ内のグローバル」は基本的に「スコープ」が意味するだけなので、有効なフレーズではありません。「グローバル」スコープの要点は、他のすべてのスコープがアクセスできるスコープであるということです。
Gareth

19

それはクロージャーと呼ばれています。基本的には関数内のコードを封印し、他のライブラリがコードに干渉しないようにします。これは、コンパイルされた言語で名前空間を作成することに似ています。

例。私が書いたとしましょう:

(function() {

    var x = 2;

    // do stuff with x

})();

これで、他のライブラリがx、ライブラリで使用するために作成した変数にアクセスできなくなりました。


7
用語に注意してください。名前空間とは、名前空間をアドレス指定することにより(通常は接頭辞を使用して)、外部から変数にアクセスできることを意味します。これはJavascriptでは可能ですが、ここでは説明していません
Gareth

名前空間とまったく同じではないことに同意しますが、公開したいプロパティを持つオブジェクトを返すことで同様の機能を提供できます(function(){ ... return { publicProp1: 'blah' }; })();。明らかに名前空間と完全に平行ではありませんが、そのように考えると役立つかもしれません。
ジョエル

あなたの例では、xはまだプライベート変数です...それをIIFEでラップしていますが。先に行くと、機能のアクセスX外にしてみてください、あなたがすることはできません...
RayLoveless

ポイントが無効です。次の関数でも、他のライブラリはxにアクセスできません。function(){var x = 2}
RayLoveless

@RayLoveless同意する。私はその主張に矛盾しません。実際、私はこの回答の最後の文と同じ主張をしました。
Joel

8

一部のhtml5オブジェクトのブラウザーサポートを決定するこの方法のように、関数クロージャーをより大きな式のデータとして使用することもできます。

   navigator.html5={
     canvas: (function(){
      var dc= document.createElement('canvas');
      if(!dc.getContext) return 0;
      var c= dc.getContext('2d');
      return typeof c.fillText== 'function'? 2: 1;
     })(),
     localStorage: (function(){
      return !!window.localStorage;
     })(),
     webworkers: (function(){
      return !!window.Worker;
     })(),
     offline: (function(){
      return !!window.applicationCache;
     })()
    }

何と!! 行う?
1.21ギガワット2017

!! 値をブール(true / false)表現に変換します。
リアム

7

変数をローカルに保つことに加えて、非常に便利な使い方の1つは、グローバル変数を使用してライブラリを作成するときに、ライブラリ内で使用する短い変数名を付けることができます。jQueryでは、jQuery.noConflict()を使用して、jQueryを指す$変数を無効にできるため、jQueryプラグインの作成によく使用されます。これが無効になっている場合でも、コードは$を使用でき、次のようにしても壊れません。

(function($) { ...code...})(jQuery);

3
  1. 同じウィンドウ内の他のメソッド/ライブラリとの衝突を回避するには、
  2. グローバルスコープを避け、ローカルスコープにします。
  3. デバッグを高速化するには(ローカルスコープ)、
  4. JavaScriptには関数スコープのみがあるため、コードのコンパイルにも役立ちます。

1

また、スコープ関数で「use strict」を使用して、コードが「strictモード」で実行されるようにする必要があります。以下に示すサンプルコード

(function() {
    'use strict';

    //Your code from here
})();

なぜ厳密を使用する必要があるのですか?
nbro 2016


本当に質問に答えません!
Pritam Banerjee 2016

プリタムは、その良い練習用です。回答を投票する前に適切な調査を行ってください
Neha Jain

1
'use strict'は、悪いプログラマーを彼ら自身から救います。そして、大多数のプログラマーは悪いプログラマーであるので、彼らが絶対にしてはならないことをして彼らがコードの急速に沈む混乱に終わるのを防ぐのを助けます。
MattE
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.