web-dev-qa-db-ja.com

JavaScriptで定義する前に関数を使用できるのはなぜですか?

このコードは、異なるブラウザーでも常に機能します。

function fooCheck() {
  alert(internalFoo()); // We are using internalFoo() here...

  return internalFoo(); // And here, even though it has not been defined...

  function internalFoo() { return true; } //...until here!
}

fooCheck();

しかし、なぜそれが機能するのかについての単一の参照を見つけることができませんでした。これは、John Resigのプレゼンテーションノートで初めて見ましたが、言及されただけです。そのことについての説明はどこにもありません。

誰かが私を啓発してもらえますか?

151
Edu Felipe

function宣言は魔法であり、コードブロック*内の何かが実行される前に識別子がバインドされます。

これは、通常のトップダウン順序で評価されるfunction式による割り当てとは異なります。

次のように例を変更した場合:

var internalFoo = function() { return true; };

動作しなくなります。

関数宣言は、ほとんど同じに見え、場合によってはあいまいになる場合がありますが、構文的には関数式とは完全に分離されています。

これは ECMAScript標準 、セクション10.1.3に記載されています。残念ながら、ECMA-262は、標準規格でも非常に読みやすい文書ではありません!

*:含まれる関数、ブロック、モジュール、またはスクリプト。

203
bobince

これはHOISTINGと呼ばれます-定義される前に関数を呼び出します(呼び出されます)。

書きたい2つの異なるタイプの関数は次のとおりです。

式関数と減速関数

  1. 式関数:

    関数式は変数に格納できるため、関数名は必要ありません。また、匿名関数(名前のない関数)としても名前が付けられます。

    (呼び出される)を呼び出すには、常に変数名を使用する必要があります。これらの種類の関数は、定義されている場所の前で呼び出すと機能しません。つまり、ここでホイストが行われていないことを意味します。常に最初に式関数を定義してから呼び出す必要があります。

    let lastName = function (family) {
     console.log("My last name is " + family);
    };
    let x = lastName("Lopez");
    

    これは、ECMAScript 6で書く方法です。

    lastName = (family) => console.log("My last name is " + family);
    
    x = lastName("Lopez");
    
  2. 減速関数:

    次の構文で宣言された関数は、すぐには実行されません。これらは「後で使用するために保存され」、後で呼び出された(呼び出された)ときに実行されます。これらのタイプの関数は、定義されている場所の前または後に呼び出すと機能します。定義されている場所-巻き上げ-の前に減速機能を呼び出すと、正常に機能します。

    function Name(name) {
      console.log("My cat's name is " + name);
    }
    Name("Chloe");
    

    巻き上げの例:

    Name("Chloe");
    function Name(name) {
       console.log("My cat's name is " + name);
    }
    
19
Negin

ブラウザーはHTMLを最初から最後まで読み取り、読み取られて実行可能チャンク(変数宣言、関数定義など)に解析されるときに実行できますが、どの時点でも、そのポイントより前のスクリプトで定義されたもののみを使用できます。

これは、すべてのソースコードを処理(コンパイル)し、おそらく参照を解決するために必要なライブラリとリンクし、実行を開始する実行可能モジュールを構築する他のプログラミングコンテキストとは異なります。

コードは、さらに定義された名前付きオブジェクト(変数、他の関数など)を参照できますが、すべてのピースが使用可能になるまで参照コードを実行できません。

JavaScriptに慣れると、適切な順序で物事を書く必要があることを深く知るようになります。

リビジョン:受け入れられた回答(上記)を確認するには、Firebugを使用してWebページのスクリプトセクションをステップスルーします。実際にコードを実行する前に、関数から関数へスキップし、最初の行のみを表示します。

13
dkretz

一部の言語では、使用前に識別子を定義する必要があるという要件があります。これは、コンパイラがソースコードで単一のパスを使用するためです。

しかし、複数のパスがある場合(または一部のチェックが延期される場合)、その要件なしで完全に生きることができます。この場合、おそらく最初にコードが読み取られ(そして解釈され)、その後リンクが設定されます。

3
Toon Krijthe

JavaScriptを少ししか使用していません。これが役立つかどうかはわかりませんが、あなたが話しているものと非常に似ており、いくつかの洞察を与える可能性があります:

http://www.dustindiaz.com/javascript-function-declaration-ambiguity/

2
RailsSon

関数「internalFoo」の本体は、解析時にどこかに移動する必要があるため、JSインタープリターがコードを読み取る(解析する)と、関数のデータ構造が作成され、名前が割り当てられます。

後になってからコードが実行され、JavaScriptは実際に「internalFoo」が存在するかどうか、それが何であるか、呼び出し可能かどうかなどを見つけようとします。

0
Aaron Digulla