64

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

Javascript で関数を引き出すには、次の 2 つの方法があります。

var foo = function() { ... }

これは少し不自然です。別の一般的なパターンは次のとおりです。

var foo = {
   baz: 43,
   doSomething: function() {
       // ...
   }
}

function foo() { 
  // ... 
}

どちらかを優先する明確な理由はありますか?

4

4 に答える 4

87

それはすべて、関数を宣言する場所の優先順位に帰着します。巻き上げ。

関数宣言と変数宣言は、JavaScriptインタープリターによって、常に見えないようにそれらを含むスコープの先頭に移動(「巻き上げ」)されます。関数パラメーターと言語定義の名前は、明らかにすでに存在しています。これは、次のようなコードを意味します。

function foo() {
    bar();
    var x = 1;
}

実際には次のように解釈されます。

function foo() {
    var x;
    bar();
    x = 1;
}

宣言の割り当て部分が引き上げられていないことに注意してください。名前だけが掲げられています。これは、関数本体全体が同様に引き上げられる関数宣言には当てはまりません。

function test() {
    foo(); // TypeError "foo is not a function"
    bar(); // "this will run!"
    var foo = function () { // function expression assigned to local variable 'foo'
        alert("this won't run!");
    }
    function bar() { // function declaration, given the name 'bar'
        alert("this will run!");
    }
}
test();

この場合、関数宣言のみが本体を上に持ち上げます。「foo」という名前が付けられていますが、実行中に割り当てられるように、本体は残されています。

関数宣言のような構文を使用して、関数式で定義された関数に名前を付けることができます。これは関数宣言にはならず、名前はスコープに入れられず、本体は持ち上げられません。

foo(); // TypeError "foo is not a function"
bar(); // valid
baz(); // TypeError "baz is not a function"
bin(); // ReferenceError "bin is not defined"

var foo = function () {}; // anonymous function expression ('foo' gets hoisted)
function bar() {}; // function declaration ('bar' and the function body get hoisted)
var baz = function bin() {}; // named function expression (only 'baz' gets hoisted)

foo(); // valid
bar(); // valid
baz(); // valid
bin(); // ReferenceError "bin is not defined"

したがって、関数を一番上に上げることを好む場合は、それ以外のfunction declaration場合はを使用しますexpression。私は通常、メソッドをとしてオブジェクトリテラルを作成するため、後者の方が好きfunction expressionsです。

名前付きfunction expressionsは、エラーがスローされたときに便利です。anonymousコンソールは、別名スタックトレースを示す代わりに、関数が何であるかを教えてくれます。

于 2012-04-10T00:57:49.613 に答える
11

あなたはここでいくつかの異なることを思いつきましたが、最初にあなたの主な質問を見つけようとします.

一般に....

function() { ... }関数式です。2構文的に、これはorと同じレベルにあります[4,5]。これはを表します。そうするvar foo=function(){ ... }ことで、毎回計画どおりに機能します。

function foo() { ... } 関数宣言です。これは と同じことを行うように見えるかもしれませんがvar foo=function(){...}、小さな注意点があります。宣言として、JS の変数ホイストの概念と同様に機能します (基本的に、すべての変数宣言は、式が評価される前に行われます)。

良い例はここからです:

function test() {
    foo(); // TypeError "foo is not a function"
    bar(); // "this will run!"
    var foo = function () { // function expression assigned to local variable 'foo'
        alert("this won't run!");
    }
    function bar() { // function declaration, given the name 'bar'
        alert("this will run!");
    }
}
test();

基本的に変数ホイストは値を一番上に持ってきたので、このコードは (理論上) と同等です:

function test() {
    var foo;//foo hoisted to top
    var bar=function(){//this as well
        alert("this will run!");
    }

    foo(); // TypeError "foo is not a function"
    bar(); // "this will run!"
    var foo = function () { // function expression assigned to local variable 'foo'
        alert("this won't run!");
    }
}

注意: 私は、JS インタープリターは理論に従うのに苦労していると言いたいと思います。ここでは、セクションの最後に、理論と実践が機能しない良い例を示します (式と宣言のトピックに関する詳細もあります)。

おもしろい事実:function foo() {...}括弧で囲むと、宣言が式に変換され、次のような奇妙なコードになる可能性があります。

(function foo() { return 1; })();// 1
foo; //ReferenceError: foo is not defined

理由がなければ、これをしないでください。


要約 var foo=function(){ ... }は*ちょっと*関数と同じfoo(){ ... }ですが、前者はあなたが思うところにあると思うことをしますが、後者は括弧で囲まない限り奇妙なことをしますが、それは範囲を台無しにします.JSインタープリターはあなたを許可します仕様で構文エラーと見なされることを行うため、間違っていることが実際には正しいと信じるようになります。

関数式( var f=function(){...}) を使用してください。特に、ドット構文を使用しているときに多少強制されることを考えると、そうしない本当の理由はありません。


2番目に触れたものに……

何を言いたいのかよくわかりませんが、これについては他のすべてとはまったく異なります。

var foo = {
    baz: 43,
    doSomething:function() {
        ...
    }
}

これは、オブジェクト リテラル構文として知られています。この構文に基づいた JSON は、data をフォーマットするための非常に巧妙な方法であり、JS のこの構文は、新しいオブジェクトを宣言するためによく使用されます。 . また、XML を使用するのと同じ方法で使用することもでき、すべてのクールな子供たちに好まれています...

とにかく、基本的にオブジェクトリテラル構文は次のように機能します。

{ name1: val1, .... namek:valk }

この式は、特定の値が初期化されたオブジェクトです。そうすることは、次のことをvar obj={ name1: val1, .... namek:valk }意味します。

obj.name1==val1;
obj['name1']==val1;// x['y'] is the same thing as x.y 
...
obj.namek==valk;

では、これは私たちの例と何の関係があるのでしょうか? 基本的に、式はシングルトン オブジェクトを宣言するためによく使用されます。しかし、オブジェクトのプロトタイプを宣言するためにも使用できるため、誰かが後で var newObj=Object.create(foo) を実行すると、newObj はプロトタイプとして foo を持つことになります。

プロトタイプ継承の有用性を実際に知りたい場合は、プロトタイプ継承を詳しく調べてください。Douglas Crockford は、彼の多くの講演の1 つでそれについて詳しく語っています)。

于 2012-04-10T01:25:01.213 に答える
2

関数に名前を付ける利点はほとんどありません

  • メタ分析のための名前。functionInstance.name名前を表示します。
  • さらに重要なことは、名前がスタック トレースに出力されることです。
  • 名前は、自己文書化や読み書きのできるコードを書くのにも役立ちます。

名前付き関数式には 1 つの欠点があります

  • IE で NFE のメモリ リークが発生する

文体の制御が少ないことを除けば、関数宣言に欠点はありません

于 2012-04-10T01:05:52.383 に答える
1

関数が変数またはプロパティに割り当てられている場合、関数を必ずしも匿名にする必要はないため、質問は実際には2つの部分で構成されています。

指名 vs 匿名?

@Raynos は要点を明確に強調しています。名前付き関数の最も良い点は、それらがスタック トレースに表示されることです。関数が変数/プロパティに割り当てられている状況でも、デバッグを容易にするために関数に名前を付けることは良い考えですが、無名関数が悪だとは言いません。それらは素晴らしい目的を果たします:

匿名関数はJavaScriptの悪い習慣ですか?

関数宣言と関数式?

質問のその部分については、おそらくこのトピックを私ができるよりもはるかに深くカバーしているため、この質問を参照してください。

var functionName = function() {} vs 関数 functionName() {}

于 2012-04-10T01:10:34.390 に答える