JavaScriptの自己実行機能の目的は何ですか?


427

JavaScriptでは、いつこれを使用しますか?

(function(){
    //Bunch of code...
})();

これ以上:

//Bunch of code...



なぜセミコロンの直前に最後の2つの括弧があるのですか?
ジョニー14

3
@johnny最後の2つの括弧の前の部分は(無名)関数を宣言しています。これらの2つの括弧は関数を呼び出します。
Ej。

7
「すぐに呼び出される関数式」またはIIFEは、これに適した名前です。
Flimm

回答:


404

それはすべて変数スコープに関するものです。自己実行関数で宣言された変数は、デフォルトでは、自己実行関数内のコードでのみ使用できます。これにより、JavaScriptコードの他のブロックで変数がどのように命名されるかを気にすることなくコードを記述できます。

たとえば、アレクサンダーのコメントで述べたように:

(function() {
  var foo = 3;
  console.log(foo);
})();

console.log(foo);

が定義されていないため3、最初にログに記録され、次にエラーがスローされます。console.logfoo


7
また、Netflixのエンジニア全員を含む多くの人々の利益のためにも:ITは単なる機能です。それ自体は閉鎖を表すものではありません。オートインボーカーがクロージャ関連のシナリオと併用されて適切な処理が行われる場合がありますが、ガベージコレクションされてクロージャ以外の言語で実行される参照を保持しているものが見つからない場合、それは何もしません。 CLOSURESでおかしくなります。
エリック・レッペン、2012

2
つまり、これは主にクロージャで使用されますか?
ピルアブドゥル

@AlexanderBird、完全に正しくはありません...なしvarで実行すると、次のようになり...function(){ foo=3;}ます。グローバル変数を設定しますよね?
T.Todua 2016年

2
@AlexanderBirdそれは、すでに関数内のローカル変数で起こる:function(){ var foo = 3; alert(foo); }; alert(foo);だから私はまだそれを得ることはありません
ジョアン・ピメンテル・フェレイラ

ああ、わかりました。一度にこれら3つの機能をすべて実現します。1)宣言するだけで関数を実行します。2)関数として、変数のスコープはローカルのみです。そして3)関数は、匿名、メインスコープ汚染ではないかもしれない
ジョアン・ピメンテルフェレイラ

94

単純化。とても正常に見えるので、ほとんど快適です:

var userName = "Sean";

console.log(name());

function name() {
  return userName;
}

ただし、高度な文字を基本レベルの表現に変換する非常に便利なJavaScriptライブラリをページに組み込んだ場合はどうなりますか?

待って...何?

つまり、誰かが何らかのアクセントの付いた文字を入力したが、プログラムに「英語」の文字AZだけが欲しいのですか?まあ...スペイン語の「ñ」とフランス語の「é」の文字は、「n」と「e」の基本文字に変換できます。

だから、誰かが私のサイトに含めることができる包括的なキャラクターコンバーターを書いています...私はそれを含めます。

1つの問題:それは私の関数と同じ「名前」と呼ばれる関数を持っています。

これは衝突と呼ばれるものです。同じスコープで同じ名前で宣言された2つの関数があります。これは避けたいです。

したがって、コードのスコープをどうにかする必要があります。

JavaScriptでコードをスコープする唯一の方法は、コードを関数でラップすることです。

function main() {
  // We are now in our own sound-proofed room and the 
  // character-converter libarary's name() function can exist at the 
  // same time as ours. 

  var userName = "Sean";

  console.log(name());

  function name() {
    return userName;
  }
}

それで問題が解決するかもしれません。これですべてが囲まれ、開始および終了中括弧内からのみアクセスできます。

私たちは関数の中に関数を持っています...これは見た目には奇妙ですが、完全に合法です。

問題は1つだけです。私たちのコードは機能しません。userName変数がコンソールにエコーされることはありません!

この問題は、既存のコードブロックの後に関数への呼び出しを追加することで解決できます...

function main() {
  // We are now in our own sound-proofed room and the 
  // character-converter libarary's name() function can exist at the 
  // same time as ours. 

  var userName = "Sean";

  console.log(name());

  function name() {
    return userName;
  }
}

main();

または前に!

main();

function main() {
  // We are now in our own sound-proofed room and the 
  // character-converter libarary's name() function can exist at the 
  // same time as ours. 

  var userName = "Sean";

  console.log(name());

  function name() {
    return userName;
  }
}

二次的な懸念:「メイン」という名前がまだ使用されていない可能性は何ですか?...とても、とてもスリムです。

もっとスコーピングが必要です。そして、main()関数を自動的に実行するいくつかの方法。

次に、自動実行機能(または自己実行、自己実行など)について説明します。

((){})();

構文は罪として厄介です。ただし、機能します。

関数定義をかっこで囲み、パラメーターリスト(別のセットまたはかっこ!)を含めると、関数呼び出しとして機能します

それでは、いくつかの自己実行構文を使用して、コードをもう一度見てみましょう。

(function main() {
  var userName = "Sean";

    console.log(name());

    function name() {
      return userName;
    }
  }
)();

したがって、あなたが読んだほとんどのチュートリアルでは、「匿名の自己実行」という用語や類似の用語が攻撃されます。

長年の専門的な開発の後、私あなたがデバッグの目的で書くすべての関数に名前を付けることを強くお勧めします。

何かがうまくいかない場合(そしてそれがうまくいかない場合)は、ブラウザーでバックトレースを確認します。スタックトレースのエントリに名前が付いていると、コードの問題を絞り込むのが常に簡単になります。

非常に長い間、それが役に立てば幸いです!


2
ありがとう:)可変プライバシーの観点から、通常の機能に関連してIIFEの利点を理解しようと、インターネットをずっと探索してきました。あなたの答えは単に最高です。最良の利点の1つは、通常の関数がまったく同じものを提供する場合、IIFE内の変数と関数が「最終的に」プライベートであることです。最後に、私はあなたの説明の過程を通過したと思います。結局のところ、IIFEは単なる機能ですが、なぜそれを使用するのか理解しました。ありがとうございました!
viery365 2016

これをとてもよく説明する時間を割いていただきありがとうございます。
MSC

いい答えだ。私はあなたの最後の点について質問があります-すべての関数に名前を付けることをお勧めするとき、それは自己実行関数でそれを行う方法があると言っていますか、それとも誰もが関数に名前を付けて呼び出すことを提案していますか?編集ああ、なるほど。これはすでに名前が付けられています。ああ。名前付きの自己実行関数の使用を正当化していることを指摘したいと思うかもしれません。
FireSBurnsmuP 2017

まあ、これは私が探していた答えは私の友人である:)
ジョアン・ピメンテルフェレイラ

これは私が探していた答えであり、受け入れ済みとマークされているものではありません。変数名の衝突についてではなく、関数名の衝突についてだと言ってもいいでしょうか。通常のnon-iife関数でさえ変数はローカルにスコープされ、グローバル変数と衝突しませんか?
Kumar Manish

32

自己呼び出し(自動呼び出しとも呼ばれます)は、関数がその定義と同時に実行される場合です。これはコアパターンであり、JavaScript開発の他の多くのパターンの基盤として機能します。

私はそれが大好きです:)

  • コードを最小限に抑えます
  • 動作とプレゼンテーションの分離を強制します
  • 名前の競合を防止するクロージャーを提供します

非常に–(なぜそれを良いと言うべきなのか?)

  • 関数を定義して一度に実行することです。
  • その自己実行関数が値を返し、その関数をパラメーターとして別の関数に渡すことができます。
  • カプセル化に適しています。
  • また、ブロックスコープにも適しています。
  • ええ、すべての.jsファイルを自己実行機能で囲み、グローバルな名前空間の汚染を防ぐことができます。;)

詳細はこちら


43
ポイント1.どうやって?ポイント2.これは、まったく異なるベストプラクティスによるものです。ポイント3.何の機能がないの?4,5,6,7。関連性?8.さて、1/8は悪くないと思います。
エリック・レッペン、2012

2
7年遅れますが、ポイント1の場合、コードはまったく削減されません。実際、関数の作成に最低2行のコードが追加されます。
YungGun 2017

1
ここでの唯一のポイントは、「名前の競合を防止するクロージャを提供すること」であり、他のすべてのポイントは、これまたはfalseの言い換えです。多分あなたの答えを簡素化できますか?
pcarvalho

19

名前空間。JavaScriptのスコープは関数レベルです。


7
スコーピングのネームスペースインスタッドを使用したため、反対投票がまだ続いています。これは、定義の問題である-例えば参照ウィキペディア(時には名前スコープと呼ばれる)コンピュータサイエンスの名前空間を、一意の識別子または記号(すなわち、名称)の論理グループを保持するために作成した抽象コンテナまたは環境です。そして、名前空間識別子は、名前にコンテキスト(コンピュータサイエンスの範囲)を提供することができる、との用語は、時々交換可能に使用されます。
クリストフ

5
JavaScriptの関数レベルのスコープは、変数名が存在するスペース、名前空間を提供します。名前空間識別子に関連付けられていない匿名の名前であることは関係ありません...
Christoph

12

暗黙のグローバルについて言及している回答はどれも信じられません。

(function(){})()構築物は、私にはこれが大きな関心事である、暗黙のグローバル保護することはできません参照http://yuiblog.com/blog/2006/06/01/global-domination/

基本的に、ファンクションブロックは、定義したすべての依存する「グローバル変数」がプログラムに限定されていることを確認します。暗黙的なグローバルの定義から保護しません。JSHintなどは、この振る舞いをどのように防ぐかについての推奨を提供できます。

より簡潔なvar App = {}構文は、同様のレベルの保護を提供し、「パブリック」ページ上ではファンクションブロックにラップされます。(この構成を使用するライブラリの実際の例については、Ember.jsまたはSproutCoreを参照してください)

限りprivate性質が行く、彼らは親切あなたが公共のフレームワークやライブラリを作成している場合を除き過大評価のですが、あなたがそれらを実装する必要がある場合は、ダグラス・クロックフォードは、いくつかの良いアイデアを持っています。


8
厳格モードは、暗黙のグローバルから保護します。これは、自動呼び出しと組み合わせることで対応できます。私は私有財産についてフープラーを理解していません。funcコンストラクター内で変数を宣言します。できました。'new'キーワードの使用を忘れることで夜も眠れない場合は、ファクトリ関数を作成してください。もう一度やりました。
エリック・レッペン、2012

8

私はすべての回答を読みましたが、非常に重要なものがここありません。キスします。主な理由は2つあります。なぜ自己実行の匿名関数が必要なのか、または「すぐに呼び出される関数式(IIFE)」の方が良いと言えます。

  1. ネームスペース管理の改善(ネームスペース汚染の回避-> JSモジュール)
  2. クロージャー(OOPから知られている、プライベートクラスメンバーのシミュレーション)

最初のものは非常によく説明されています。2つ目については、次の例を検討してください。

var MyClosureObject = (function (){
  var MyName = 'Michael Jackson RIP';
  return {
    getMyName: function () { return MyName;},
    setMyName: function (name) { MyName = name}
  }
}());

注意1:関数をMyClosureObjectに割り当てているわけではありません。さらに、その関数を呼び出した結果です()最後の行で注意してください。

注意2: JavaScriptの関数についてさらに知っておくべきことは、内部関数が関数のパラメーターと変数アクセスできることです。これらは内部で定義されています。

いくつかの実験をしてみましょう:

私はMyName使うことができgetMyName、それはうまくいきます:

 console.log(MyClosureObject.getMyName()); 
 // Michael Jackson RIP

次の巧妙なアプローチは機能しません。

console.log(MyClosureObject.MyName); 
// undefined

しかし、別の名前を設定して、期待される結果を得ることができます。

MyClosureObject.setMyName('George Michael RIP');
console.log(MyClosureObject.getMyName()); 
// George Michael RIP

編集:上記の例でMyClosureObjectは、new接頭辞なしで使用するように設計されているため、慣例により大文字にしないでください。


7

パラメータがあり、「コードのブランチ」は関数を返しますか?

var a = function(x) { return function() { document.write(x); } }(something);

閉鎖。の値はsomething、に割り当てられた関数によって使用されaます。somethingいくつかの変化する値(forループ)を持つ可能性があり、aに新しい関数があるたびに。


+1; ただし、パラメーターとしてよりもvar x = something;外部関数で明示的な方が好きxです。imoの方がこの方法で読みやすくなります...
Christoph

@Christoph:関数が作成された後で「何か」の値が変更された場合、作成時の値ではなく、新しい値が使用されます。
stesch 2009

@stesch:どこから手に入れたの?私の知る限り、そうではありません。JSで実際の参照を取得する唯一の方法は、arguments-objectを使用することですが、それでもすべてのブラウザーで機能するわけではありません
Christoph

@Christoph: "JavaScript:The Good Parts"、Douglas Crockford(O'Reilly)
stesch

@stesch:記述したとおりに機能しません。変数を削除xし、字句スコープに直接依存する場合、新しい値が使用されます。つまり、document.write(something)...
Christoph

6

おそらくスコープの分離。関数宣言内の変数が外部の名前空間を汚染しないように。

もちろん、世の中にあるJS実装の半分については、とにかくそうなるでしょう。


4
それらはどのような実装でしょうか?
Matthew Crumley、

1
厳密モードで記述されておらず、グローバルになる原因となる暗黙のvar宣言を含む実装。
エリックレッペン、

5

自己呼び出しの匿名関数がどのように役立つかを示す確かな例を次に示します。

for( var i = 0; i < 10; i++ ) {
  setTimeout(function(){
    console.log(i)
  })
}

出力: 10, 10, 10, 10, 10...

for( var i = 0; i < 10; i++ ) {
  (function(num){
    setTimeout(function(){
      console.log(num)
    })
  })(i)
}

出力: 0, 1, 2, 3, 4...


あなたは、コードの最初のセットのために何が起こっているかについて、より多くのビットを説明することができます
radio_head

let代わりにvar最初のケース大丈夫でしょう。
Vitaly Zdanevich 2018

3

1つの違いは、関数で宣言する変数はローカルであるため、関数を終了すると変数が消え、他のコードの他の変数と競合しないことです。


1

JavaScriptの関数はファーストクラスのオブジェクトなので、そのように定義することにより、C ++やC#によく似た「クラス」を効果的に定義します。

その関数は、ローカル変数を定義し、その中に関数を持つことができます。内部関数(事実上インスタンスメソッド)はローカル変数(事実上インスタンス変数)にアクセスできますが、それらはスクリプトの残りの部分から分離されます。


1

JavaScriptの自己呼び出し関数:

自己呼び出し式は、呼び出されることなく、自動的に呼び出されます(開始されます)。自己呼び出し式は、作成された直後に呼び出されます。これは基本的に、名前の競合を回避するため、およびカプセル化を実現するために使用されます。変数または宣言されたオブジェクトは、この関数の外部からはアクセスできません。最小化(filename.min)の問題を回避するには、常に自己実行関数を使用してください。


1

自己実行関数は、変数のスコープを管理するために使用されます。

変数のスコープは、それが定義されているプログラムの領域です。

グローバル変数にはグローバルスコープがあります。これは、JavaScriptコードのあらゆる場所で定義されており、関数内でも、スクリプト内のどこからでもアクセスできます。一方、関数内で宣言された変数は、関数の本体内でのみ定義されます。これらはローカル変数であり、ローカルスコープを持ち、その関数内でのみアクセスできます。関数パラメーターもローカル変数としてカウントされ、関数の本体内でのみ定義されます。

以下に示すように、関数内でグローバル変数変数にアクセスできます。また、関数の本体内では、ローカル変数が同じ名前のグローバル変数よりも優先されることに注意してください。

var globalvar = "globalvar"; // this var can be accessed anywhere within the script

function scope() {
    alert(globalvar);
    localvar = "localvar" //can only be accessed within the function scope
}

scope(); 

したがって、基本的に自己実行機能により、JavaScriptコードの他のブロックで変数がどのように命名されるかを気にせずにコードを記述できます。


1
(function(){
    var foo = {
        name: 'bob'
    };
    console.log(foo.name); // bob
})();
console.log(foo.name); // Reference error

実際には、上記の関数は名前のない関数式として扱われます。

関数を閉じ括弧と開き括弧でラップする主な目的は、グローバル空間を汚染しないようにすることです。

関数式内の変数と関数はプライベートになりました(つまり、関数の外では使用できなくなります)。


1

短い答えは: グローバル(またはそれ以上の)スコープの汚染を防ぐことです。

IIFE(Immediately Invoked Function Expressions)は、プラグイン、アドオン、ユーザースクリプト、または他の人のスクリプトで動作することが予想されるスクリプトとしてスクリプトを記述するためのベストプラクティスです。これにより、定義した変数が他のスクリプトに望ましくない影響を与えないことが保証されます。

これは、IIFE式を記述するもう1つの方法です。私は個人的にこの次の方法を好みます:

void function() {
  console.log('boo!');
  // expected output: "boo!"
}();

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void

例から、それ以上に一度だけ実行されることが期待される機能があるため、生命維持にも効率性とパフォーマンスに影響を与えることができることは非常に明確で一度実行して、良いのための空隙にダンプされます。つまり、関数またはメソッドの宣言はメモリに残りません。


ニース、私はvoid以前のその使用法を見ていませんでした。私はそれが好きです。
Ej。

1

まず、MDN IIFEにアクセスする必要があります。

  • これは即時に呼び出される関数式です。したがって、JavaScriptファイルがHTMLから呼び出されると、この関数はすぐに呼び出されます。
  • これにより、IIFEイディオム内の変数にアクセスしたり、グローバルスコープを汚染したりすることがなくなります。

0

この質問に対する回答はすべて準備できているようですが、とにかく私の入力を投稿します。

自己実行機能をいつ使用したいのかわかります。

var myObject = {
    childObject: new function(){
        // bunch of code
    },
    objVar1: <value>,
    objVar2: <value>
}

この関数を使用すると、追加のコードを使用して、よく使用される変数の設定や数学の方程式の実行など、よりクリーンなコードのchildObjects属性とプロパティを定義できます。ああ!またはエラーチェック。ネストされたオブジェクトのインスタンス化構文に限定されるのとは対照的に...

object: {
    childObject: {
        childObject: {<value>, <value>, <value>}
    }, 
    objVar1: <value>,
    objVar2: <value>
}

一般に、コーディングには同じことを行うための多くのあいまいな方法があり、「なぜわざわざ?」しかし、ベーシック/コアプリンシパルだけに依存できなくなった新しい状況が次々と現れています。


0

あなたの簡単な質問を考えてみましょう:「JavaScriptでは、いつこれを使いたいですか:...」

私は@ken_browningと@sean_holdingの回答が好きですが、ここでは言及されていない別のユースケースを示します。

let red_tree = new Node(10);

(async function () {
    for (let i = 0; i < 1000; i++) {
        await red_tree.insert(i);
    }
})();

console.log('----->red_tree.printInOrder():', red_tree.printInOrder());

ここで、Node.insertは非同期アクションです。

関数の宣言でasyncキーワードなしで単にawaitを呼び出すことはできません。後で使用するために名前付き関数は必要ありませんが、そのinsert呼び出しを待つ必要があります。 。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.