web-dev-qa-db-ja.com

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

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

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

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

558
Andrew Kou

通常は名前空間を設定し(後述)、メンバー関数や変数の可視性を制御します。それをオブジェクト定義のように考えてください。 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() { ... code ... })()を書いたときにしていることは、関数リテラルの中にコードを作成していることです(つまり、「オブジェクト」全体が実際には関数であることを意味します)。その後、あなたは自分自身で関数を呼び出しています(最後の())。だから私が前に述べたようにこれの主な利点は、あなたがプライベートメソッド/関数とプロパティを持つことができるということです。

(function() {
   var private_var;

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

最初の例では、globalFunction()がパブリック機能にアクセスするために呼び出すことができるパブリック関数でしたが、上記の例ではどのようにしてそれを呼び出しますか?ここで自己起動関数はコードを起動時に自動的に実行させます。 initMyStuff()を追加できるのと同じように。名前のない関数なのでinitMyStuff()のように複数回呼び出すことはできませんが、この自己起動関数はグローバルスコープの一部として自動的に実行されます。

きちんとしたことは、物事を内部で定義して外の世界に公開することもできるということです(名前空間の例ですので、基本的にあなた自身のライブラリ/プラグインを作成することができます):

var myPlugin = (function() {
 var private_var;

 function private_function() {
 }

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

これでmyPlugin.public_function1()を呼び出せますが、private_function()にアクセスすることはできません。クラス定義と非常によく似ています。これをよりよく理解するために、さらに読むために以下のリンクをお勧めします。

EDIT

私は言及するのを忘れました。最後の()では、内部に必要なものをすべて渡すことができます。たとえば、jQueryプラグインを作成するときは、次のようにjQueryまたは$を渡します。

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

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

  • グローバルパラメータを再定義して、ローカルスコープで意味のある名前を付けることができます。
  • スコープチェーンをグローバルスコープに辿っていくよりも、ローカルスコープで物事を調べる方が速いので、パフォーマンス上わずかな利点があります。
  • 圧縮(縮小)には利点があります。

先に、これらの関数が起動時に自動的に実行される方法を説明しましたが、もしそれらが自動的に実行されるなら誰が引数を渡しているのでしょうか?この手法では、すべてのパラメータがグローバル変数として定義されていると想定しています。そのため、jQueryがグローバル変数として定義されていないと、この例は機能せず、この例は無名関数なので他の方法で呼び出すことはできません。ご想像のとおり、jquery.jsの初期化中に行うことの1つは、jQueryグローバル変数を定義することです。これは、より有名な$グローバル変数を定義することです。

762
Vivin Paliath

要するに

概要

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

それはの可能性を減らすのに役立ちます:

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

それはドキュメントの準備ができたことを検出しません - それはある種のdocument.onloadでもwindow.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:The Good Parts"から)。


もっと詳しく

代替コード

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

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

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

 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 scope(ブラウザ環境ではwindowの範囲)しかありません

Javascript Scopesについてもっと読む:


リソース


次のステップ

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

75
Adrien Be

ブラウザ内のJavascriptには、実際には2つの有効範囲しかありません。機能範囲とグローバル範囲です。

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

26
Gareth

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

例です。私が書いたとします。

(function() {

    var x = 2;

    // do stuff with x

})();

現在、他のライブラリは、自分のライブラリで使用するために作成した変数xにアクセスできません。

19
Joel

大きなクロージャーでは、関数クロージャーをdataとして使用することもできます。この方法では、一部の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;
     })()
    }
8
kennebec

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

(function($) { ...code...})(jQuery);
7
Coronus
  1. 同じウィンドウ内で他のメソッド/ライブラリと衝突しないようにするには、
  2. グローバルスコープを避け、ローカルスコープにします。
  3. デバッグを速くするために(ローカルスコープ)、
  4. JavaScriptは関数スコープのみを持っているので、コードのコンパイルにも役立ちます。
3
Vivek Mehta

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

(function() {
    'use strict';

    //Your code from here
})();
1
Neha Jain