47

重複の可能性:
JavaScript: var functionName = function() {} vs function functionName() {}

JavaScript で、変数を関数として定義する目的は何ですか? この規則は以前に見たことがありますが、完全には理解していません。

たとえば、スクリプトのある時点で、関数は次のように呼び出されます。

whatever();

しかし、次のように、という名前の関数が表示されると予想される場所whatever:

function whatever(){

}

代わりにwhatever、次のように、関数として定義されているという変数が表示されます。

var whatever = function(){

}

これの目的は何ですか?関数に名前を付けるだけでなく、なぜこれを行うのですか?

4

5 に答える 5

38

:回答の最後にある更新を参照してください。ブロック内の宣言が有効になりました(ただし、厳密モードを使用していない場合は非常に複雑になります)。


これが1つの理由です:

var whatever;

if (some_condition) {
    whatever = function() {
        // Do something
    };
}
else {
    whatever = function() {
        // Do something else
    };
}
whatever();

ライブラリの初期化で、実装の違い(Webブラウザー間の違い、IEattachEventと標準の違いなど)を処理する必要があるようなコードが表示される場合がありますaddEventListener。関数宣言で同等のことを行うことはできません。

if (some_condition) {
    function whatever() {    // <=== DON'T DO THIS
        // Do something
    }
}
else {
    function whatever() {    // <=== IT'S INVALID
        // Do something else
    }
}
whatever();

...これらは制御構造内で指定されていないため、JavaScriptエンジンは必要な処理を実行でき、エンジンが異なれば実行も異なります。(編集:繰り返しますが、以下の注を参照してください。これらは現在指定されています。)

これとは別に、

var whatever = function() {
    // ...
};

function whatever() {
    // ...
}

1つ目は関数式であり、コンテキストの段階的な実行(たとえば、含まれている関数、またはグローバルコードの段階的な実行)でコードがそのポイントに到達したときに評価されます。また、無名関数になります(それを参照する変数には名前がありますが、関数には名前がありません。これは、ツールが役立つことを意味します)。

2つ目は関数宣言であり、コンテキストに入ると、ステップバイステップのコードが実行される前に評価されます。(これを「巻き上げ」と呼ぶ人もいます。これは、ソースのさらに下の何かが、ソースの上の何かよりも早く発生するためです。)関数にも固有名が付けられています。

したがって、次のことを考慮してください。

function foo() {
    doSomething();
    doSomethingElse();
    console.log("typeof bar = " + typeof bar); // Logs "function"

    function bar() {
    }
}

一方

function foo() {
    doSomething();
    doSomethingElse();
    console.log("typeof bar = " + typeof bar); // Logs "undefined"

    var bar = function() {
    };
}

最初の例では、宣言を使用して、および他の段階的なコードが実行される前に宣言が処理されます。doSomething2番目の例では、これはであるため、段階的なコードの一部として実行され、関数は上で定義されていません(変数varも「持ち上げられている」ため、上で定義されています)。

そして、まとめ:今のところ、一般的なクライアント側のWebではこれを行うことはできません。

var bar = function foo() { // <=== Don't do this in client-side code for now
    // ...
};

これができるはずです。これは名前付き関数式と呼ばれ、関数に適切な名前を付ける関数式です。しかし、さまざまなJavaScriptエンジンがさまざまな時期にそれを誤解しており、IEはごく最近まで実際に非常に誤解を続けていました


ES2015+のアップデート

ES2015(別名「ES6」)の時点で、ブロック内の関数宣言が仕様に追加されました。

厳密モード

厳密モードでは、新しく指定された動作は単純で理解しやすいものです。それらは、それらが発生するブロックにスコープされ、その上部に持ち上げられます。

したがって、この:

"use strict";
if (Math.random() < 0.5) {
  foo();
  function foo() {
    console.log("low");
  }
} else {
  foo();
  function foo() {
    console.log("high");
  }
}
console.log(typeof foo); // undefined

(関数の呼び出しがブロック内の関数のにあることに注意してください。)

...基本的にこれと同等です:

"use strict";
if (Math.random() < 0.5) {
  let foo = function() {
    console.log("low");
  };
  foo();
} else {
  let foo = function() {
    console.log("high");
  };
  foo();
}
console.log(typeof foo); // undefined

ルーズモード

ルーズモードの動作ははるかに複雑であり、さらに理論的には、WebブラウザのJavaScriptエンジンとWebブラウザではないJavaScriptエンジンの間で異なります。ここでは入りません。ただそれをしないでください。ブロック内の関数宣言を主張する場合は、厳密モードを使用してください。厳密モードでは、それらが意味をなし、環境全体で一貫しています。

于 2012-02-27T15:01:05.377 に答える
3

これは、関数を変数に格納し、たとえばそれらをパラメーターとして他の関数に渡すことができるようにするためです。これが役立つ1つの例は、引数としてコールバックを渡す非同期関数を作成する場合です。

var callback = function() { console.log('done', result)}

var dosomething = function(callback) {
    //do some stuff here
    ...
    result = 1;
    callback(result);
}

関数はJavaScriptのオブジェクトであるため、プロパティやメソッドを使用して関数を拡張することもできます。

于 2012-02-27T15:04:42.507 に答える
1

JavaScriptの関数はオブジェクトです。つまり、それらは値です。したがって、関数の定義方法に関係なく、関数を参照する変数をいつでも設定できます。

function foo() { ... }

var anotherFoo = foo;
anotherFoo(); // calls foo

関数は、オブジェクトプロパティ、関数パラメーター、配列要素、およびJavaScriptで一般的な値が実行できるその他のすべてとして使用できる値です。それらはオブジェクトであり、独自のプロパティを持つこともできます。

于 2012-02-27T15:01:43.117 に答える
1

関数を変数に割り当てると、それを引数として他の関数に渡したり、Javascriptのオブジェクトモデルを利用するように拡張したりできます。

于 2012-02-27T15:02:14.783 に答える
0

関数内で「var」を使用して関数変数を宣言すると、変数にはその関数内でのみアクセスできます。関数を終了すると、変数は破棄されます。これらの変数はローカル変数と呼ばれます。それぞれが宣言されている関数によってのみ認識されるため、異なる関数に同じ名前のローカル変数を含めることができます。

于 2012-02-27T15:03:58.777 に答える