web-dev-qa-db-ja.com

名前付き関数式を使用する理由

JavaScriptで関数式を実行するには、2つの異なる方法があります。

名前付き関数式(NFE)

_var boo = function boo () {
  alert(1);
};
_

匿名関数式

_var boo = function () {
  alert(1);
};
_

そして、両方ともboo();で呼び出すことができます。匿名関数を使用する理由と時期、および名前付き関数式を使用する時期が本当にわかりません。それらの間にはどんな違いがありますか?

89

無名関数式の場合、関数はanonymous—文字通り、名前はありません。割り当てる変数には名前がありますが、関数にはありません。 (更新:ES5でもそうでした。ES2015[別名ES6]の時点で、匿名式で作成された関数はしばしば真の名前(自動識別子ではない)を取得します。 。)

名前は便利です。名前は、スタックトレース、コールスタック、ブレークポイントのリストなどで見ることができます。名前はGood Thing™です。

(以前のバージョンのIE [IE8以下]の名前付き関数式に注意する必要がありました。2つのまったく異なる時間に2つの完全に別個の関数オブジェクトを誤って作成したためです。 article Double take ]。IE8 [!!]をサポートする必要がある場合は、おそらく匿名関数式を使用することをお勧めしますまたはfunctiondeclarations、ただし、名前付き関数式は避けてください。

名前付き関数式に関する重要なことの1つは、関数本体内の関数の名前でスコープ内識別子を作成することです。

var x = function example() {
    console.log(typeof example); // "function"
};
x();
console.log(typeof example);     // "undefined"

ただし、ES2015の時点では、多くの「匿名」関数式が名前付きの関数を作成します。これは、コンテキストから名前を推測するのが非常に賢明な、さまざまな最新のJavaScriptエンジンによって先行されていました。 ES2015では、匿名関数式はbooという名前の関数になります。ただし、ES2015 +セマンティクスを使用しても、自動識別子は作成されません。

var obj = {
    x: function() {
       console.log(typeof x);   // "undefined"
       console.log(obj.x.name); // "x"
    },
    y: function y() {
       console.log(typeof y);   // "function"
       console.log(obj.y.name); // "y"
    }
};
obj.x();
obj.y();

関数名の割り当ては、仕様のさまざまな操作で使用される SetFunctionName abstract操作で行われます。

基本的に、短いバージョンは、次のような匿名関数式が割り当てや初期化のようなものの右側に表示されるときです。

var boo = function() { /*...*/ };

(またはletではなくconstまたはvarにすることができます)、または

var obj = {
    boo: function() { /*...*/ }
};

または

doSomething({
    boo: function() { /*...*/ }
});

(最後の2つは実際には同じものです)、結果の関数は名前(例ではboo)になります。

重要な、意図的な例外があります:既存のオブジェクトのプロパティへの割り当て:

obj.boo = function() { /*...*/ }; // <== Does not get a name

これは、新しい機能が追加される過程を経たときに発生した情報漏洩の懸念によるものです。別の質問への私の答えの詳細 here

81
T.J. Crowder

関数の名前付けは、自分自身を参照する必要がある場合に役立ちます(たとえば、再帰呼び出しの場合)。実際、リテラル関数式を引数として別の関数に直接渡す場合、その関数式cannotは、名前が付けられていない限り、ES5 strictモードでそれ自体を直接参照します。

たとえば、次のコードを検討してください。

_setTimeout(function sayMoo() {
    alert('MOO');
    setTimeout(sayMoo, 1000);
}, 1000);
_

setTimeoutに渡される関数式が匿名の場合、このコードをこれほどきれいに記述することはできません。代わりにsetTimeout呼び出しの前に変数に割り当てる必要があります。このように、名前付き関数式を使用すると、少し短くなります。

_arguments.callee_ ...を活用することで、匿名関数式を使用しても、このようなコードを書くことが歴史的に可能でした。

_setTimeout(function () {
    alert('MOO');
    setTimeout(arguments.callee, 1000);
}, 1000);
_

...しかし_arguments.callee_は非推奨であり、ES5 strictモードでは完全に禁止されています。したがって、MDNは次のことを推奨します。

関数式に名前を付けるまたは関数を使用することにより、arguments.callee()を使用しないでください関数がそれ自体を呼び出さなければならない宣言。

(エンファシス鉱山)

22
Mark Amery

関数が関数式として指定されている場合、名前を付けることができます。

関数内でのみ使用できます(IE8-を除く)。

_var f = function sayHi(name) {
  alert( sayHi ); // Inside the function you can see the function code
};

alert( sayHi ); // (Error: undefined variable 'sayHi')
_

この名前は、別の変数に書き込まれた場合でも、信頼できる再帰関数呼び出しを目的としています。

さらに、NFE(名前付き関数式)名は、次のようにObject.defineProperty(...)メソッドで上書きできます。

_var test = function sayHi(name) {
  Object.defineProperty(test, 'name', { value: 'foo', configurable: true });
  alert( test.name ); // foo
};

test();
_

注:関数宣言では、これを行うことはできません。この「特別な」内部関数名は、関数式構文でのみ指定されます。

3
Roman

常に名前付きを使用関数式を使用する必要があります。それが理由です:

  1. 再帰が必要な場合は、その関数の名前を使用できます。

  2. 匿名関数は、問題の原因となる関数の名前が表示されないため、デバッグ時に役立ちません。

  3. 関数に名前を付けない場合、後でその機能を理解するのが難しくなります。名前を付けると理解しやすくなります。

var foo = function bar() {
    //some code...
};

foo();
bar(); // Error!

ここでは、たとえば、名前バーは関数式内で使用されるため、外部スコープで宣言されません。名前付き関数式では、関数式の名前は独自のスコープ内に含まれます。

2
lux

arguments.calleeなどの非推奨の機能に依存することなく、問題の関数を参照できるようにする場合は、名前付き関数式を使用することをお勧めします。

1