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


6874

最近、他人のJavaScriptコードの保守を始めました。私はバグを修正し、機能を追加し、コードを整理してより一貫性のあるものにしようとしています。

以前の開発者は2つの方法で関数を宣言していましたが、その背後に理由があるかどうかはわかりません。

2つの方法は次のとおりです。

var functionOne = function() {
    // Some code
};
function functionTwo() {
    // Some code
}

これらの2つの異なる方法を使用する理由は何ですか、それぞれの長所と短所は何ですか?ある方法でできること、他の方法ではできないことはありますか?


198
permadi.com/tutorial/jsFunc/index.htmlは、JavaScript関数に関する非常に優れたページです
uzay95

67
関連するのは、名前付き関数式に関するこの優れた記事です。
Phrogz

12
@CMSがこの記事を参照しています:kangax.github.com/nfe/#expr-vs-decl
Upperstage

106
次の2つの点に注意する必要があります。#1 JavaScriptでは、宣言が巻き上げられます。それが意味var a = 1; var b = 2;になりvar a; var b; a = 1; b = 2。そのため、functionOneを宣言すると宣言されますが、その値はすぐには設定されません。一方、functionTwoは単なる宣言なので、スコープの先頭に配置されます。#2 functionTwoを使用すると、nameプロパティにアクセスできます。これは、何かをデバッグするときに非常に役立ちます。
xavierm02 2012

65
ああ、ところで、正しい構文は ";"です。割り当て後、宣言後なし。例えばfunction f(){}var f = function(){};
xavierm02

回答:


5045

違いはfunctionOne、関数式であり、その行に達したときにのみ定義されるのに対し、functionTwoは関数宣言であり、周囲の関数またはスクリプトが実行されるとすぐに定義されます(ホイストにより)。

たとえば、関数式:

// TypeError: functionOne is not a function
functionOne();

var functionOne = function() {
  console.log("Hello!");
};

そして、関数宣言:

// Outputs: "Hello!"
functionTwo();

function functionTwo() {
  console.log("Hello!");
}

従来、ブロック内で定義された関数宣言は、ブラウザー間で一貫性のない方法で処理されていました。厳密モード(ES5で導入)は、関数宣言をそれらの外側のブロックにスコープすることでこれを解決しました。

'use strict';    
{ // note this block!
  function functionThree() {
    console.log("Hello!");
  }
}
functionThree(); // ReferenceError


632
@Greg:ちなみに、違いはそれらが異なる時に解析されることだけではありません。本質的に、あなたfunctionOneは無名関数が割り当てられた単なる変数ですが、functionTwo実際には名前付き関数です。.toString()両方を呼び出して違いを確認してください。これは、関数の名前をプログラムで取得したい場合に重要です。
Jason Bunting 2011

6
@Jason Bunting ..ここで何を取得しているのかわからない、.toString()は両方に対して基本的に同じ値(関数定義)を返すようです: cl.ly/2a2C2Y1r0J451o0q0B1B
Jon z

124
両方が異なります。最初のものはfunction expression、2番目のものはfunction declarationです。あなたはここにトピックの詳細を読むことができます:javascriptweblog.wordpress.com/2010/07/06/...
ミハルKuklis

127
@Greg解析時間と実行時間に関するあなたの答えの一部は正しくありません。JavaScriptでは、関数宣言は解析時には定義されず、実行時に定義されます。プロセスは次のようになります。ソースコードが解析されます-> JavaScriptプログラムが評価されます->グローバル実行コンテキストが初期化されます->宣言バインディングのインスタンス化が実行されます。このプロセス中に、関数宣言がインスタンス化されます(第10.5章のステップ5を参照)。
–ŠimeVidas、2012

102
この現象の用語は、巻き上げとして知られています。
Colin Pear 2013年

1942

まず、Gregを修正したいと思います。function abc(){}スコープもスコープです—名前abcは、この定義が検出されたスコープで定義されています。例:

function xyz(){
  function abc(){};
  // abc is defined here...
}
// ...but not here

次に、両方のスタイルを組み合わせることができます。

var xyz = function abc(){};

xyzは通常どおりに定義されabcますが、Internet Explorerを除くすべてのブラウザーでは未定義です。定義されているとは限りません。しかし、それはその本体の中で定義されます:

var xyz = function abc(){
  // xyz is visible here
  // abc is visible here
}
// xyz is visible here
// abc is undefined here

すべてのブラウザで関数のエイリアスを作成する場合は、次の種類の宣言を使用します。

function abc(){};
var xyz = abc;

この場合、xyzおよびabcは同じオブジェクトのエイリアスです。

console.log(xyz === abc); // prints "true"

結合スタイルを使用する1つの説得力のある理由は、関数オブジェクトの「名前」属性です(Internet Explorerではサポートされていません)。基本的にあなたがのような関数を定義するとき

function abc(){};
console.log(abc.name); // prints "abc"

その名前は自動的に割り当てられます。しかし、次のように定義すると

var abc = function(){};
console.log(abc.name); // prints ""

その名前は空です—匿名関数を作成し、それをいくつかの変数に割り当てました。

結合されたスタイルを使用するもう1つの理由は、短い内部名を使用してそれ自体を参照する一方で、外部ユーザーに競合しない長い名前を提供することです。

// Assume really.long.external.scoped is {}
really.long.external.scoped.name = function shortcut(n){
  // Let it call itself recursively:
  shortcut(n - 1);
  // ...
  // Let it pass itself as a callback:
  someFunction(shortcut);
  // ...
}

上記の例では、外部名でも同じことができますが、扱いにくく(遅く)なります。

(それ自体を参照するもう1つの方法は、を使用することですarguments.callee。これはまだ比較的長く、strictモードではサポートされていません。)

JavaScriptは両方のステートメントを異なる方法で扱います。これは関数宣言です:

function abc(){}

abc ここは現在のスコープのどこでも定義されています:

// We can call it here
abc(); // Works

// Yet, it is defined down there.
function abc(){}

// We can call it again
abc(); // Works

また、それはreturn声明を通じて浮上しました:

// We can call it here
abc(); // Works
return;
function abc(){}

これは関数式です:

var xyz = function(){};

xyz ここでは、割り当てのポイントから定義されています。

// We can't call it here
xyz(); // UNDEFINED!!!

// Now it is defined
xyz = function(){}

// We can call it here
xyz(); // works

関数宣言と関数式の違いは、Gregが実証した違いがある本当の理由です。

楽しい事実:

var xyz = function abc(){};
console.log(xyz.name); // Prints "abc"

個人的には、この方法で可視性を制御できるため、「関数式」宣言を好みます。のような関数を定義すると

var abc = function(){};

私は関数をローカルで定義したことを知っています。のような関数を定義すると

abc = function(){};

abcスコープのチェーンのどこにも定義しなかった場合は、グローバルに定義したことを知っています。この定義スタイルは、内部で使用されeval()た場合でも回復力があります。定義しながら

function abc(){};

特に状況に応じて、コンテキストに依存し、実際にどこで定義されているかを推測する可能性がありますeval()—答えは:ブラウザーに依存します。


69
私はRoBorgに言及していますが、彼はどこにもいません。シンプル:RoBorg === Greg。それがインターネットの時代に歴史を書き換えることができる方法です。;-)
ユージンラズトキン、

10
var xyz = function abc(){}; console.log(xyz === abc); 私がテストしたすべてのブラウザー(Safari 4、Firefox 3.5.5、Opera 10.10)では、「未定義の変数:abc」が表示されます。
NVI 2009

7
全体的に、この投稿は、関数宣言を利用することの違いと利点を説明するのにうまく機能していると思います。特に「利点」はグローバルエンティティを宣言することの賛成であるように思われるため、変数への関数式の割り当てを利用することの利点に関する限り、同意しないことに同意します。 、 正しい?;-)
natlee75 2013年

83
名前付き関数を使用する大きな理由は、デバッガーが名前を使用して、コールスタックまたはスタックトレースを理解するのに役立つためです。コールスタックを見て、「無名関数」が10レベルの深さであるのを見るとうんざりします
ヤギ

3
var abc = function(){}; console.log(abc.name);""これ以上は生成せず、"abc"代わりに生成します。
Qwerty、

632

関数を作成する標準フォームの概要は次のとおりです(元々は別の質問のために書かれましたが、正規の質問に移動した後に適応されました)

条項:

クイックリスト:

  • 関数宣言

  • 「匿名」function(用語にもかかわらず、名前付きの関数を作成する場合があります)

  • 名前付きfunction

  • アクセサ関数初期化子(ES5 +)

  • アロー関数式(ES2015 +)(無名関数式のように、明示的な名前は含まれていませんが、名前付きの関数を作成できます)

  • オブジェクト初期化子でのメソッド宣言(ES2015 +)

  • class(ES2015 +)のコンストラクターとメソッドの宣言

関数宣言

最初の形式は、次のような関数宣言です。

function x() {
    console.log('x');
}

関数宣言は宣言です。ステートメントや式ではありません。したがって、それに続くのは;(無害ですが)ありません。

関数宣言は、ステップバイステップのコードが実行されるに、実行が出現するコンテキストに入ったときに処理されます。作成される関数には適切な名前が付けられ(x上記の例では)、その名前は宣言が現れるスコープに置かれます。

同じコンテキストでステップバイステップのコードの前に処理されるため、次のようなことができます:

x(); // Works even though it's above the declaration
function x() {
    console.log('x');
}

ES2015までは、仕様では、あなたのような制御構造内の関数宣言を置けば、JavaScriptエンジンが何をすべきかをカバーしていないtryifswitchwhileなど、このような:

if (someCondition) {
    function foo() {    // <===== HERE THERE
    }                   // <===== BE DRAGONS
}

また、これらはステップバイステップのコードが実行される前に処理されるため、制御構造内にあるときに何をすべきかを知るのは難しいです。

これを行うことはES2015まで指定されていませんでしたが、ブロックでの関数宣言をサポートするための許容可能な拡張でした。残念なことに(そして必然的に)、エンジンが異なれば異なることも行われました。

ES2015の時点で、仕様には何をすべきかが記載されています。実際、次の3つのことを行うことができます。

  1. Webブラウザー上ではないルーズモードの場合、JavaScriptエンジンは1つのことを行うことになっています
  2. Webブラウザーでルーズモードの場合、JavaScriptエンジンは他のことを行うことになっています
  3. した場合、厳密なモード(ブラウザまたはしない)、JavaScriptエンジンは、また別のことを行うことになっています

ルーズモードのルールはトリッキーですが、ストリクトモードでは、ブロックでの関数宣言は簡単です。これらはブロックに対してローカルであり(ES2015でも新しく追加されたブロックスコープを持ち)、先頭に移動しますブロックの。そう:

"use strict";
if (someCondition) {
    foo();               // Works just fine
    function foo() {
    }
}
console.log(typeof foo); // "undefined" (`foo` is not in scope here
                         // because it's not in the same block)

「匿名」function表現

2番目の一般的な形式は、無名関数式と呼ばれます

var y = function () {
    console.log('y');
};

すべての式と同様に、コードの段階的な実行で到達したときに評価されます。

ES5では、これによって作成される関数には名前がありません(匿名)。ES2015では、可能であればコンテキストから推測することで、関数に名前が割り当てられます。上記の例では、名前はになりますy。関数がプロパティ初期化子の値である場合、同様のことが行われます。(これが発生するタイミングとルールの詳細については、仕様を検索しSetFunctionNameてください。 それは至る所に表示されます。)

名前付きfunction

3番目の形式は、名前付き関数式( "NFE")です。

var z = function w() {
    console.log('zw')
};

これが作成する関数には適切な名前があります(wこの場合)。すべての式と同様に、これは、コードの段階的な実行で到達したときに評価されます。関数の名前は、式が表示されるスコープに追加されません。名前関数自体のスコープ内にあります:

var z = function w() {
    console.log(typeof w); // "function"
};
console.log(typeof w);     // "undefined"

NFEは、JavaScript実装のバグの原因となることが多いことに注意してください。たとえば、IE8以前では、NFEを完全に誤って処理し、2つの異なる時点で2つの異なる関数を作成します。Safariの初期バージョンにも問題がありました。良いニュースは、ブラウザの現在のバージョン(IE9以降、現在のSafari)では、これらの問題がなくなったことです。(しかし、これを書いている時点で残念なことに、IE8は依然として広く使用されているため、Web用のコードでNFEを使用することには依然として問題があります。)

アクセサ関数初期化子(ES5 +)

時々、機能がほとんど気付かれずにこっそり入ることがあります。これは、アクセサ関数の場合です。次に例を示します。

var obj = {
    value: 0,
    get f() {
        return this.value;
    },
    set f(v) {
        this.value = v;
    }
};
console.log(obj.f);         // 0
console.log(typeof obj.f);  // "number"

この関数を使用したときは使用しなかったことに注意してください()。これは、プロパティのアクセサ関数であるためです。通常の方法でプロパティを取得および設定しますが、裏で関数が呼び出されます。

、、およびあまり知られていない第2引数を使用してObject.defineProperty、アクセサ関数を作成することもできます。Object.definePropertiesObject.create

アロー関数式(ES2015 +)

ES2015は、矢印機能をもたらします。以下はその一例です。

var a = [1, 2, 3];
var b = a.map(n => n * 2);
console.log(b.join(", ")); // 2, 4, 6

通話にn => n * 2隠れていることを確認しmap()ますか?それは関数です。

アロー関数に関するいくつかのこと:

  1. 彼らは自分自身を持っていませんthis。その代わりに、彼らは近くにわたりthis、それらが定義されているコンテキストの。(また、それらは近くにargumentsあり、関連する場合)super。これは、thisそれらの内部thisがそれらが作成された場所と同じであり、変更できないことを意味します。

  2. 上記でお気づきのように、キーワードは使用しませんfunction。代わりに、を使用します=>

上記のn => n * 2例はそれらの1つの形式です。関数を渡す引数が複数ある場合は、括弧を使用します。

var a = [1, 2, 3];
var b = a.map((n, i) => n * i);
console.log(b.join(", ")); // 0, 2, 6

Array#map最初の引数としてエントリを渡し、2番目の引数としてインデックスを渡すことを忘れないでください。)

どちらの場合も、関数の本体は単なる式です。関数の戻り値は自動的にその式の結果になります(明示的なは使用しませんreturn)。

1つ以上の式を実行している場合は、通常どおり{}、明示的にreturn(値を返す必要がある場合)を使用します。

var a = [
  {first: "Joe", last: "Bloggs"},
  {first: "Albert", last: "Bloggs"},
  {first: "Mary", last: "Albright"}
];
a = a.sort((a, b) => {
  var rv = a.last.localeCompare(b.last);
  if (rv === 0) {
    rv = a.first.localeCompare(b.first);
  }
  return rv;
});
console.log(JSON.stringify(a));

なしのバージョン{ ... }は、式本体または簡潔な本体を持つ矢印関数と呼ばれます。(また:簡潔な機能矢印)を有するもの{ ... }体を定義が有する矢印関数である関数本体。(また:詳細な矢印関数。)

オブジェクト初期化子でのメソッド宣言(ES2015 +)

ES2015では、メソッド定義と呼ばれる関数を参照するプロパティをより短い形式で宣言できます。次のようになります。

var o = {
    foo() {
    }
};

ES5以前でほぼ同等のものは次のようになります。

var o = {
    foo: function foo() {
    }
};

違い(冗長性以外)は、メソッドはを使用できますsuperが、関数は使用できないことです。したがって、たとえば、valueOfメソッド構文を使用して(たとえば)定義されたオブジェクトがある場合super.valueOf()、値を取得するためObject.prototype.valueOfに(おそらく他の何かを実行する前に)オブジェクトが返されますが、ES5バージョンはObject.prototype.valueOf.call(this)代わりに行う必要があります。

これは、メソッドがそれが定義されたオブジェクトへの参照を持っていることも意味します。そのため、そのオブジェクトが一時的である場合(たとえば、Object.assignソースオブジェクトの1つとしてそれを渡す場合)、メソッド構文はオブジェクトが保持されることを意味します。それ以外の場合はガベージコレクションされた可能性のあるメモリ内(JavaScriptエンジンがその状況を検出せず、どのメソッドもを使用しない場合は処理しますsuper)。

class(ES2015 +)のコンストラクターとメソッドの宣言

ES2015はclass、宣言されたコンストラクターとメソッドを含む構文を提供します。

class Person {
    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    getFullName() {
        return this.firstName + " " + this.lastName;
    }
}

上記の2つの関数宣言があります。1つは名前を取得するコンストラクタPerson用でgetFullName、もう1つはに割り当てられた関数Person.prototypeです。


3
その後、名前wは単に無視されますか?
BiAiB 2014年

8
@PellePenna:関数名は多くのことに役立ちます。私の見解では、再帰と、コールスタック、例外トレースなどに表示される関数の名前の2つが重要です。
TJクラウダー2014年

4
@ChaimEliyah-「受け入れることは、それが最良の答えであることを意味するのではなく、質問した人のために機能したことを意味します。」出典
ScrapCode、2016

6
@AR:まさにそうです。ただ面白いことに、そのすぐ上には、「ベストアンサーが最初に表示されるので、いつでも簡単に見つけられる」と書かれています。承認された回答は投票数の多い回答よりも最初に表示されるため、ツアーは多少自己矛盾する可能性があります。;-)また、投票によって「ベスト」を決定した場合(これは信頼できません。それは私たちが得たものにすぎません)、「投票」タブを使用している場合にのみ「ベスト」の回答が最初に表示されます。 -それ以外の場合、最初の回答はアクティブな回答、または最も古い回答です。
TJクラウダー2016

1
@TJCrowder:そうですね。「日付順」は時々迷惑です。
ScrapCode 2016

144

グローバルコンテキストについて言えば、varステートメントとFunctionDeclaration末尾のa は両方ともグローバルオブジェクトに削除不可のプロパティを作成しますが、両方の値は上書きできます

2つの方法の微妙な違いは、変数のインスタンス化プロセスが実行されると(実際のコード実行の前に)で宣言されたすべての識別子varがで初期化undefinedされ、で使用されている識別子がFunctionDeclarationその瞬間から利用可能になることです。たとえば、次のようになります。

 alert(typeof foo); // 'function', it's already available
 alert(typeof bar); // 'undefined'
 function foo () {}
 var bar = function () {};
 alert(typeof bar); // 'function'

の割り当ては、bar FunctionExpression実行時まで行われます。

によって作成されたグローバルプロパティはFunctionDeclaration、変数値のように問題なく上書きできます。例:

 function test () {}
 test = null;

2つの例のもう1つの明らかな違いは、最初の関数には名前がありませんが、2番目の関数には名前があることです。これは、デバッグ(つまり、コールスタックの検査)時に非常に役立ちます。

編集した最初の例(foo = function() { alert('hello!'); };)については、宣言されていない割り当てですvar。常にキーワードを使用することを強くお勧めします。

varステートメントなしの割り当てでは、参照された識別子がスコープチェーンに見つからない場合、グローバルオブジェクトの削除可能なプロパティになります。

また、宣言されていない割り当ては、Strict ModeのReferenceError ECMAScript 5でをスローします

必読:

:この回答は別の質問からマージされたもので、OPからの主な疑念と誤解は、で宣言された識別子はFunctionDeclaration上書きできないということでしたが、そうではありません。


JavaScriptで関数が上書きされる可能性があることを知りませんでした!また、その解析順序は私にとって大きなセールスポイントです。関数の作成方法を監視する必要があると思います。
Xeoncross 2010

2
+0は404ingなので、「Names function expression demystified」の記事に追加します。考えられるミラー?:kangax.github.com/nfe
Mr_Chimp

@CMSいいですね。原作を見たことがないので覚えておいてください。それが鏡なのか、同じタイトルの別の記事なのかはわかりません。
Mr_Chimp

@Mr_Chimp確かにそうです。thewaybackmachineは、クロール時に302を取得し、リダイレクトは指定したリンクへのリダイレクトであったと言っています。
John

124

投稿した2つのコードスニペットは、ほとんどすべての目的で同じように動作します。

ただし、動作の違いは、最初のバリアント(var functionOne = function() {})では、その関数はコード内のそのポイントの後でしか呼び出せないことです。

2番目のバリアント(function functionTwo())を使用すると、関数は、関数が宣言されている場所より上で実行されるコードで使用できます。

これは、最初のバリアントでは、関数がfoo実行時に変数に割り当てられるためです。2番目では、関数はfoo解析時にその識別子に割り当てられます。

その他の技術情報

JavaScriptには、関数を定義する3つの方法があります。

  1. 最初のスニペットは関数式を示しています。これには、「関数」演算子を使用して関数を作成することが含まれます。その演算子の結果は、任意の変数またはオブジェクトプロパティに格納できます。関数式はそのように強力です。関数式は、名前を付ける必要がないため、「無名関数」と呼ばれることがよくあります。
  2. 2番目の例は関数宣言です。これは、「関数」ステートメントを使用して関数を作成します。関数は解析時に使用可能になり、そのスコープ内のどこからでも呼び出すことができます。後で変数またはオブジェクトプロパティに保存することもできます。
  3. 関数を定義する3番目の方法は、「Function()」コンストラクターです。これは、元の投稿には示されていません。これにeval()は問題があると同じように機能するため、これを使用することはお勧めしません。

103

グレッグの答えに対するより良い説明

functionTwo();
function functionTwo() {
}

エラーが発生しないのはなぜですか?式は上から下に実行されることを常に教えられました(??)

なぜなら:

関数宣言と変数宣言はhoisted、JavaScriptインタープリターによって常にそれらを含むスコープの最上部に見えないように()移動されます。関数のパラメーターと言語で定義された名前は、既に存在しています。ベンチェリー

これは、次のようなコードを意味します。

functionOne();                  ---------------      var functionOne;
                                | is actually |      functionOne();
var functionOne = function(){   | interpreted |-->
};                              |    like     |      functionOne = function(){
                                ---------------      };

宣言の割り当て部分が持ち上げられていないことに注意してください。名前だけが巻き上げられます。

ただし、関数宣言の場合は、関数本体全体も巻き上げられます

functionTwo();              ---------------      function functionTwo() {
                            | is actually |      };
function functionTwo() {    | interpreted |-->
}                           |    like     |      functionTwo();
                            ---------------

機能トピックについての明確な情報をありがとうございます。私の質問は、変数宣言(functionOne)と関数宣言(functionTwo)のどちらが宣言階層の最初の宣言になるかです。
Sharathi RB 2016

91

他のコメンターは、上記の2つのバリアントの意味上の違いをすでにカバーしています。文体の違いに注意したいと思います。「割り当て」バリエーションのみが別のオブジェクトのプロパティを設定できます。

私はよくこのようなパターンでJavaScriptモジュールをビルドします:

(function(){
    var exports = {};

    function privateUtil() {
            ...
    }

    exports.publicUtil = function() {
            ...
    };

    return exports;
})();

このパターンを使用すると、パブリック関数はすべて割り当てを使用し、プライベート関数は宣言を使用します。

(割り当てではステートメントの後にセミコロンが必要ですが、宣言ではそれが禁止されていることにも注意してください。)


4
yuiblog.com/blog/2007/06/12/module-patternは、私が知る限り、モジュールパターンの原始的なリファレンスです。(その記事を使用していますけどvar foo = function(){...}でも、プライベート変数の構文を。
ショーン・マクミラン

これは、一部の古いバージョンのIEでは完全には当てはまりません。(ことfunction window.onload() {}だった。)
Ry-

77

最初の方法を2番目の方法より優先する場合の例は、関数の以前の定義を上書きしないようにする必要がある場合です。

if (condition){
    function myfunction(){
        // Some code
    }
}

、この定義はmyfunction、解析時に行われるため、以前の定義を上書きします。

ながら

if (condition){
    var myfunction = function (){
        // Some code
    }
}

が満たされたmyfunction場合にのみ定義するという正しい仕事をしますcondition


1
この例は良いですし、完璧に近いですが、改善することができます。より良い例はvar myFunc = null;、ループの外側、またはif / elseif / elseブロックの外側で定義することです。その後、条件に応じて同じ変数に異なる関数を割り当てることができます。JSでは、欠損値をnullに割り当ててから未定義に割り当てることをお勧めします。したがって、最初にmyFunctionをnullとして宣言し、それを後で条件付きで割り当てる必要があります。
Alexander Mills

62

重要な理由は、名前空間の「ルート」として変数を1つだけ追加することです...

var MyNamespace = {}
MyNamespace.foo= function() {

}

または

var MyNamespace = {
  foo: function() {
  },
  ...
}

ネームスペースには多くのテクニックがあります。利用可能なJavaScriptモジュールが多すぎると、それはより重要になります。

また、JavaScriptで名前空間を宣言するにどうすればよいですか?


3
この回答は別の質問からこの質問に統合されたようです。表現この質問とは少し関係がないように見えるかもしれません。回答を編集して、具体的にこの質問に向けられるようにしますか?(繰り返しますが、これはまったくあなたの責任ではありません...結合された質問の副作用です)。それを削除することもでき、あなたの評判を維持すると思います。または、そのままにしておくこともできます。古いので大きな違いはないかもしれません。
Andrew Barber

55

ホイスト は、すべての変数と関数の宣言を現在のスコープの最上部に移動するJavaScriptインタープリターのアクションです。

ただし、実際の宣言のみが巻き上げられます。彼らがいる場所に課題を残すことによって。

  • ページ内で宣言された変数/関数はグローバルであり、そのページのどこにでもアクセスできます。
  • 関数内で宣言された変数/関数はローカルスコープを持っています。これらは、関数本体(スコープ)内で使用/アクセスできることを意味します。関数本体外では使用できません。

変数

JavaScriptは、緩やかに型付けされた言語と呼ばれます。つまり、JavaScript変数は任意のData-Typeの値を保持できます。Javascriptは、実行時に提供される値/リテラル​​に基づいて、変数タイプの変更を自動的に処理します。

global_Page = 10;                                               var global_Page;      « undefined
    « Integer literal, Number Type.   -------------------       global_Page = 10;     « Number         
global_Page = 'Yash';                 |   Interpreted   |       global_Page = 'Yash'; « String
    « String literal, String Type.    «       AS        «       global_Page = true;   « Boolean 
var global_Page = true;               |                 |       global_Page = function (){          « function
    « Boolean Type                    -------------------                 var local_functionblock;  « undefined
global_Page = function (){                                                local_functionblock = 777 Number
    var local_functionblock = 777;                              };  
    // Assigning function as a data.
};  

関数

function Identifier_opt ( FormalParameterList_opt ) { 
      FunctionBody | sequence of statements

      « return;  Default undefined
      « return 'some data';
}
  • ページ内で宣言された関数は、グローバルアクセスが可能なページの上部に移動します。
  • function-block内で宣言された関数は、ブロックの最上部に巻き上げられます。
  • 関数のデフォルトの戻り値は「未定義」、変数宣言のデフォルト値も「未定義」

    Scope with respect to function-block global. 
    Scope with respect to page undefined | not available.

関数宣言

function globalAccess() {                                  function globalAccess() {      
}                                  -------------------     }
globalAccess();                    |                 |     function globalAccess() { « Re-Defined / overridden.
localAccess();                     «   Hoisted  As   «         function localAccess() {
function globalAccess() {          |                 |         }
     localAccess();                -------------------         localAccess(); « function accessed with in globalAccess() only.
     function localAccess() {                              }
     }                                                     globalAccess();
}                                                          localAccess(); « ReferenceError as the function is not defined

関数式

        10;                 « literal
       (10);                « Expression                (10).toString() -> '10'
var a;                      
    a = 10;                 « Expression var              a.toString()  -> '10'
(function invoke() {        « Expression Function
 console.log('Self Invoking');                      (function () {
});                                                               }) () -> 'Self Invoking'

var f; 
    f = function (){        « Expression var Function
    console.log('var Function');                                   f ()  -> 'var Function'
    };

変数に割り当てられた関数例:

(function selfExecuting(){
    console.log('IIFE - Immediately-Invoked Function Expression');
}());

var anonymous = function (){
    console.log('anonymous function Expression');
};

var namedExpression = function for_InternalUSE(fact){
    if(fact === 1){
        return 1;
    }

    var localExpression = function(){
        console.log('Local to the parent Function Scope');
    };
    globalExpression = function(){ 
        console.log('creates a new global variable, then assigned this function.');
    };

    //return; //undefined.
    return fact * for_InternalUSE( fact - 1);   
};

namedExpression();
globalExpression();

として解釈されるJavaScript

var anonymous;
var namedExpression;
var globalExpression;

anonymous = function (){
    console.log('anonymous function Expression');
};

namedExpression = function for_InternalUSE(fact){
    var localExpression;

    if(fact === 1){
        return 1;
    }
    localExpression = function(){
        console.log('Local to the parent Function Scope');
    };
    globalExpression = function(){ 
        console.log('creates a new global variable, then assigned this function.');
    };

    return fact * for_InternalUSE( fact - 1);    // DEFAULT UNDEFINED.
};

namedExpression(10);
globalExpression();

あなたは関数宣言をチェックすることができます、異なるブラウザでの式テストを使用して jsperf Test Runner


ES5コンストラクター関数クラス:Function.prototype.bindを使用して作成された関数オブジェクト

JavaScriptは関数をファーストクラスのオブジェクトとして扱うため、オブジェクトであるため、プロパティを関数に割り当てることができます。

function Shape(id) { // Function Declaration
    this.id = id;
};
    // Adding a prototyped method to a function.
    Shape.prototype.getID = function () {
        return this.id;
    };
    Shape.prototype.setID = function ( id ) {
        this.id = id;
    };

var expFn = Shape; // Function Expression

var funObj = new Shape( ); // Function Object
funObj.hasOwnProperty('prototype'); // false
funObj.setID( 10 );
console.log( funObj.getID() ); // 10

ES6で導入されたArrow関数Arrow関数の式は構文が短く、メソッド以外の関数に最適であり、コンストラクターとして使用できません。

ArrowFunction : ArrowParameters => ConciseBody

const fn = (item) => { return item & 1 ? 'Odd' : 'Even'; };
console.log( fn(2) ); // Even
console.log( fn(3) ); // Odd

3
ああ、あなたの答えは...あいまいではないですか?よく書かれているので、費やして情報を書きすぎた場合は+1。
デンマーク語

40

他の皆が巻上部分を完全にカバーしたからといって、私は自分の答えを追加しています。

私は長い間、どちらの方法が優れているのか疑問に思っていました。http://jsperf.comのおかげで、私は知っています:)

ここに画像の説明を入力してください

関数の宣言はより速く、それはウェブ開発で本当に重要なことですよね?;)


8
ほとんどのコードでは、保守性が最も重要な側面だと思います。パフォーマンスは重要ですが、ほとんどの場合、IOは関数を定義する方法よりも大きなボトルネックになる可能性があります。ただし、取得できるすべてのパフォーマンスを必要とするいくつかの問題があり、このような場合に役立ちます。また、質問の明確に定義された部分に明確に答える回答があるとよいでしょう。
Richard Garside

3
まあ、私はそれがFirefoxの別の方法であることがわかりました。jsperf.com/sandytest
Sandeep Nayak

単なる更新です。JavaScriptで完全な関数型プログラミングスタイルを使用しているため、宣言を使用することはなく、関数式のみを使用して、変数名で関数をチェーンして呼び出すことができます。RamdaJSをチェックしてください...
Leon

1
@SandeepNayak私はFirefox 50.0.0 / Windows 7 0.0.0で独自のテストを実行しましたが、実際にはLeonのテストと同じです。したがって、あなたのテストが正しい場合、jsperfのテストは指標ではなく、すべてがブラウザーやOSのバージョン、またはその特定の瞬間の現在のマシンの特定の状態に依存すると結論付けます。
ocramot 2017年

33

バインディングが確立されると、関数宣言と変数に割り当てられた関数式は同じように動作します。

ただし、関数オブジェクトが実際にその変数に関連付けられる方法タイミングには違いがあります。この違いは、JavaScriptの変数ホイストと呼ばれるメカニズムによるものです。

基本的に、すべての関数宣言と変数宣言は、宣言が発生する関数の先頭に移動します(これが、JavaScriptに関数スコープがあると言う理由です)。

  • 関数宣言がホイストされると、関数本体が「フォロー」するため、関数本体が評価されると、変数はすぐに関数オブジェクトにバインドされます。

  • 変数宣言がホイストされると、初期化は行われ ず、「取り残されます」。変数はundefined関数本体の先頭で初期化さ れ 、コード内の元の場所に値が割り当てられます。(実際には、同じ名前の変数の宣言が行われるすべての場所で値が割り当てられます。)

巻き上げの順序も重要です。関数の宣言は同じ名前の変数の宣言よりも優先され、最後の関数の宣言は同じ名前の以前の関数の宣言よりも優先されます。

いくつかの例...

var foo = 1;
function bar() {
  if (!foo) {
    var foo = 10 }
  return foo; }
bar() // 10

変数はfoo、関数の先頭に掲揚に初期化されるundefinedので、それが!fooあるtrueので、foo割り当てられています10foo外側barのスコープは何の役割も果たさず、そのままです。

function f() {
  return a; 
  function a() {return 1}; 
  var a = 4;
  function a() {return 2}}
f()() // 2

function f() {
  return a;
  var a = 4;
  function a() {return 1};
  function a() {return 2}}
f()() // 2

関数宣言は変数宣言よりも優先され、最後の関数宣言は「固定」されます。

function f() {
  var a = 4;
  function a() {return 1}; 
  function a() {return 2}; 
  return a; }
f() // 4

この例でaは、2番目の関数宣言を評価した結果の関数オブジェクトで初期化され、次にが割り当てられ4ます。

var a = 1;
function b() {
  a = 10;
  return;
  function a() {}}
b();
a // 1

ここでは、関数宣言が最初に巻き上げられ、変数を宣言して初期化しますa。次に、この変数が割り当てられ10ます。つまり、割り当ては外部変数に割り当てませんa


3
閉じ括弧を配置する少し奇妙な方法があります。あなたはPythonコーダーですか?JavascriptをPythonのように見せようとしているようです。他の人を混乱させていると思います。私があなたのJavaScriptコードを維持しなければならなかったなら、私はあなたのコードに自動プリティプリンターを最初に通過させました。
nalply

1
素晴らしいポスト。「自己実行関数」または「すぐに呼び出される関数式」は簡単に見られるはずであり、スタイルの好みは投稿を損なうものであってはなりません。これは正確であり、「巻き上げ」を完全に要約しています。+1
リカルシン2013

32

最初の例は関数宣言です:

function abc(){}

2番目の例は関数式です。

var abc = function() {};

主な違いは、それらがどのように持ち上げられるか(持ち上げられ、宣言される)です。最初の例では、関数宣言全体が巻き上げられます。2番目の例では、var 'abc'のみが巻き上げられ、その値(関数)は未定義になり、関数自体は宣言された位置に残ります。

簡単に言えば:

//this will work
abc(param);
function abc(){}

//this would fail
abc(param);
var abc = function() {}

このトピックについて詳しく学ぶには、このリンクを強くお勧めします


1
あなたの例では、トップの答えと同じのようなものである
後藤

この回答を投稿する主な理由は、下部にリンクを提供することでした。これは、上記の質問を完全に理解するために欠けていた部分でした。
sla55er 2014年

1
あなたがリンクを共有したかったのはとてもクールです。ただし、SOの追加情報へのリンクは、質問またはお気に入りの回答のいずれかに対するコメントにする必要があります。このように長くて複雑なページに、情報の繰り返しがあり、ページの最後に便利なリンクを1つ追加するだけでは、あまり最適ではありません。いいえ、リンクを提供するための担当者ポイントは取得できませんが、コミュニティを支援することになります。
XML

31

コードのメンテナンスコストの観点からは、名前付き関数の方が望ましいです。

  • それらが宣言されている場所とは無関係です(ただし、スコープによって制限されます)。
  • 条件付き初期化などのミスに対する耐性が高まります(必要に応じて、オーバーライドすることもできます)。
  • スコープ機能とは別にローカル関数を割り当てることにより、コードが読みやすくなります。通常、スコープでは機能が最初に実行され、その後にローカル関数の宣言が続きます。
  • デバッガーでは、「匿名/評価済み」関数ではなく、コールスタックに関数名が明確に表示されます。

名前付き関数のより多くのPROSが続くと思います。名前付き関数の利点として挙げられているのは、匿名関数の欠点です。

歴史的に、匿名関数は、名前付き関数を持つメンバーをリストするための言語としてのJavaScriptの無力から現れました。

{
    member:function() { /* How do I make "this.member" a named function? */
    }
}

2
確認するテストがあります:blog.firsov.net/2010/01/…JSパフォーマンステスト-スコープと名前付き関数-分析
Sasha Firsov

25

コンピュータサイエンスの用語では、無名関数と名前付き関数について説明します。最も重要な違いは、無名関数は名前にバインドされていないため、名前の無名関数が名前であるということです。JavaScriptでは、実行時に動的に宣言される最初のクラスオブジェクトです。

匿名関数とラムダ計算の詳細については、Wikipediaが良いスタートです(http://en.wikipedia.org/wiki/Anonymous_function)。


ES2015(回答が投稿されてから6年半)の時点で、質問の両方の関数に名前が付けられています。
TJクラウダー

25

私は非常に特殊な理由でコードで変数アプローチを使用していますが、その理論は上記の抽象的な方法でカバーされていますが、JavaScriptの専門知識が限られた私のような人にとっては例が役立つかもしれません。

160の独立して設計されたブランディングで実行する必要があるコードがあります。ほとんどのコードは共有ファイルにありますが、ブランド固有のものは、ブランドごとに1つずつ、個別のファイルにあります。

特定の機能が必要なブランディングもあれば、そうでないブランディングもあります。時々私は新しいブランド固有のことをするために新しい機能を追加しなければなりません。共有コードを変更できてうれしいですが、ブランドファイルの160セットすべてを変更する必要はありません。

変数構文を使用することで、共有コードで変数(基本的には関数ポインター)を宣言し、簡単なスタブ関数を割り当てるか、nullに設定できます。

関数の特定の実装を必要とする1つまたは2つのブランドは、関数のバージョンを定義し、必要に応じてこれを変数に割り当てることができ、残りは何もしません。共有コードで実行する前に、null関数をテストできます。

上記の人々のコメントから、静的関数を再定義することも可能かもしれないと私は収集しますが、変数の解決策は素晴らしく明確です。


25

グレッグの答えは十分ですが、ダグラス・クロックフォードのビデオを見て今学んだことをさらに追加したいと思います。

関数式:

var foo = function foo() {};

関数ステートメント:

function foo() {};

functionステートメントはvarfunction値を持つステートメントの省略形にすぎません。

そう

function foo() {};

に拡大する

var foo = function foo() {};

これはさらに次のように拡張されます。

var foo = undefined;
foo = function foo() {};

そして、それらは両方ともコードの最上部まで引き上げられます。

ビデオからのスクリーンショット


7
申し訳ありませんが、これは正しくありません-私はそのスライドでクロックフォードが何を言おうとしているのかわかりません。関数と変数の両方の宣言は、常にそれらのスコープの最上部まで引き上げられます。違いは、変数の割り当て(文字列、ブール値、関数のいずれを使用して割り当てても)が上に移動しないのに対し、関数本体(関数宣言を使用)は上に移動することです。
Thomas Heymann、2015年

次のコード例をご覧ください
Thomas Heymann

24

次に示すように、関数の2つの異なる宣言には4つの注目すべき比較があります。

  1. 関数の可用性(スコープ)

function add()スコープが最も近いブロックに限定されているため、次のように機能します。

try {
  console.log("Success: ", add(1, 1));
} catch(e) {
  console.log("ERROR: " + e);
}

function add(a, b){
  return a + b;
}

関数値が変数に割り当てられる前に変数が呼び出されるため、以下は機能しませんadd

try {
  console.log("Success: ", add(1, 1));
} catch(e) {
  console.log("ERROR: " + e);
}

var add=function(a, b){
  return a + b;
}

上記のコードは、機能的に以下のコードと同じです。add = undefined単純に行うことvar add;はとまったく同じであるため、明示的に割り当てることは不必要であることに注意してくださいvar add=undefined

var add = undefined;

try {
  console.log("Success: ", add(1, 1));
} catch(e) {
  console.log("ERROR: " + e);
}

add = function(a, b){
  return a + b;
}

var add=スーパーシードがであるため、以下は機能しませんfunction add()

try {
  console.log("Success: ", add(1, 1));
} catch(e) {
  console.log("ERROR: " + e);
}

var add=function add(a, b){
  return a + b;
}

  1. (関数) .name

このように宣言されている場合、関数の名前function thefuncname(){}funcnameです。

function foobar(a, b){}

console.log(foobar.name);

var a = function foobar(){};

console.log(a.name);

それ以外の場合、関数がとして宣言されているfunction(){}と、関数 .nameが関数の格納に使用される最初の変数になります。

var a = function(){};
var b = (function(){ return function(){} });

console.log(a.name);
console.log(b.name);

関数に変数が設定されていない場合、関数名は空の文字列("")になります。

console.log((function(){}).name === "");

最後に、関数が割り当てられた変数は最初に名前を設定しますが、関数に設定された連続する変数は名前を変更しません。

var a = function(){};
var b = a;
var c = b;

console.log(a.name);
console.log(b.name);
console.log(c.name);

  1. パフォーマンス

GoogleのV8とFirefoxのSpidermonkeyでは、数マイクロ秒のJISTコンパイルの違いがあるかもしれませんが、最終的には結果はまったく同じです。これを証明するために、2つの空白のコードスニペットの速度を比較して、マイクロベンチマークでのJSPerfの効率を調べてみましょう。JSPerfテストはここにあります。また、jsben.chテストはここにあります。ご覧のように、何もないはずなのに顕著な違いがあります。あなたが本当に私のようなパフォーマンスフリークである場合、スコープ内の変数と関数の数を減らし、特にポリモーフィズム(2つの異なる型を格納するために同じ変数を使用するなど)を削減しようとする間、それはあなたの価値があるかもしれません。

  1. 変数の可変性

varキーワードを使用して変数を宣言すると、そのように変数に別の値を再割り当てできます。

(function(){
    "use strict";
    var foobar = function(){}; // initial value
    try {
        foobar = "Hello World!"; // new value
        console.log("[no error]");
    } catch(error) {
        console.log("ERROR: " + error.message);
    }
    console.log(foobar, window.foobar);
})();

ただし、const-statementを使用すると、変数参照は不変になります。つまり、変数に新しい値を割り当てることはできません。ただし、これによって変数の内容が不変になるわけではないことに注意してください。そうした場合const arr = []でも、そうすることができますarr[10] = "example"。以下のように、arr = "new value"またはのようなことのみを行うとarr = []、エラーがスローされます。

(function(){
    "use strict";
    const foobar = function(){}; // initial value
    try {
        foobar = "Hello World!"; // new value
        console.log("[no error]");
    } catch(error) {
        console.log("ERROR: " + error.message);
    }
    console.log(foobar, window.foobar);
})();

興味深いことに、変数をとして宣言すると、変数function funcName(){}の不変性はで宣言した場合と同じになりvarます。

(function(){
    "use strict";
    function foobar(){}; // initial value
    try {
        foobar = "Hello World!"; // new value
        console.log("[no error]");
    } catch(error) {
        console.log("ERROR: " + error.message);
    }
    console.log(foobar, window.foobar);
})();

「最も近いブロック」とは

「最も近いブロック」は最も近い「関数」です(非同期関数、ジェネレーター関数、非同期ジェネレーター関数を含みます)。ただし、興味深いことに、このクロージャーの外側のアイテムに対する非クロージャーブロック内にいる場合とfunction functionName() {}同様に動作var functionName = function() {}します。観察する。

  • 正常 var add=function(){}

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}');
  }
} catch(e) {
  console.log("Is a block");
}
var add=function(a, b){return a + b}

  • 正常 function add(){}

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}')
  }
} catch(e) {
  console.log("Is a block");
}
function add(a, b){
  return a + b;
}

  • 関数

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}')
  }
} catch(e) {
  console.log("Is a block");
}
(function () {
    function add(a, b){
      return a + b;
    }
})();

  • ステートメント(例えばifelseforwhiletry/ catch/ finallyswitchdo/ whilewith

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}')
  }
} catch(e) {
  console.log("Is a block");
}
{
    function add(a, b){
      return a + b;
    }
}

  • 矢印関数 var add=function()

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}')
  }
} catch(e) {
  console.log("Is a block");
}
(() => {
    var add=function(a, b){
      return a + b;
    }
})();

  • アロー機能あり function add()

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}')
  }
} catch(e) {
  console.log("Is a block");
}
(() => {
    function add(a, b){
      return a + b;
    }
})();


これは、受け入れられ、最も賛成された答えになるに値します
アーロンジョンサブ

18

@EugeneLazutkinは、自身への内部参照として使用できるように割り当てられた関数shortcut()名前付ける例を示しています。John Resigは別の例を示しています- 彼のLearning Advanced Javascriptチュートリアルで別のオブジェクト割り当てられた再帰関数をコピーしています。プロパティへの関数の割り当ては厳密にはここでは問題ではありませんが、チュートリアルを積極的に試すことをお勧めします。右上隅のボタンをクリックしてコードを実行し、コードをダブルクリックして好みに合わせて編集します。

チュートリアルの例:の再帰呼び出しyell()

元の忍者オブジェクトが削除されると、テストは失敗します。(13ページ)

var ninja = { 
  yell: function(n){ 
    return n > 0 ? ninja.yell(n-1) + "a" : "hiy"; 
  } 
}; 
assert( ninja.yell(4) == "hiyaaaa", "A single object isn't too bad, either." ); 

var samurai = { yell: ninja.yell }; 
var ninja = null; 

try { 
  samurai.yell(4); 
} catch(e){ 
  assert( false, "Uh, this isn't good! Where'd ninja.yell go?" ); 
}

再帰的に呼び出される関数に名前を付けると、テストに合格します。(14ページ)

var ninja = { 
  yell: function yell(n){ 
    return n > 0 ? yell(n-1) + "a" : "hiy"; 
  } 
}; 
assert( ninja.yell(4) == "hiyaaaa", "Works as we would expect it to!" ); 

var samurai = { yell: ninja.yell }; 
var ninja = {}; 
assert( samurai.yell(4) == "hiyaaaa", "The method correctly calls itself." );

17

他の回答で言及されていない別の違いは、匿名関数を使用する場合

var functionOne = function() {
    // Some code
};

そしてそれをコンストラクタとして使用します

var one = new functionOne();

その後one.constructor.nameは定義されません。Function.name非標準ですが、Firefox、Chrome、その他のWebkit派生ブラウザおよびIE 9以降でサポートされています。

function functionTwo() {
    // Some code
}
two = new functionTwo();

を使用すると、コンストラクタの名前を文字列として取得できますtwo.constructor.name


最初のケースの名前は、その無名関数が変数に割り当てられているため、定義されません。匿名という言葉は、名前が定義されていないもののために考案されたと思います:)
Om Shankar

この例では、var
Waqas Tahirが

16

最初のもの(関数doSomething(x))はオブジェクト表記の一部である必要があります。

2番目の(var doSomething = function(x){ alert(x);})は、無名関数を作成し、それを変数に割り当てるだけdoSomethingです。したがって、doSomething()は関数を呼び出します。

関数宣言関数式が何であるかを知りたい場合があります。

関数宣言は、変数の割り当てを必要とせずに名前付き関数変数を定義します。関数の宣言はスタンドアロンの構造として発生し、非関数ブロック内にネストすることはできません。

function foo() {
    return 3;
}

ECMA 5(13.0)は、構文を
関数識別子(FormalParameterList opt){FunctionBody} として定義します

上記の条件では、関数名はそのスコープとその親のスコープ内に表示されます(それ以外の場合は到達できません)。

そして関数式で

関数式は、より大きな式構文(通常は変数の割り当て)の一部として関数を定義します。関数式で定義された関数には、名前を付けることも、匿名にすることもできます。関数式は「関数」で始めることはできません。

// Anonymous function expression
var a = function() {
    return 3;
}

// Named function expression
var a = function foo() {
    return 3;
}

// Self-invoking function expression
(function foo() {
    alert("hello!");
})();

ECMA 5(13.0)は、構文を
関数Identifier opt(FormalParameterList opt){FunctionBody} として定義します


16

以下の違いをリストアップしています:

  1. 関数宣言は、コードのどこにでも配置できます。コード内に定義が現れる前に呼び出された場合でも、ページ内の他のコードが実行を開始する前に、関数宣言がメモリにコミットされるか、または巻き上げられる方法で実行されます。

    以下の関数を見てください:

    function outerFunction() {
        function foo() {
           return 1;
        }
        return foo();
        function foo() {
           return 2;
        }
    }
    alert(outerFunction()); // Displays 2

    これは、実行中、次のようになるためです。

    function foo() {  // The first function declaration is moved to top
        return 1;
    }
    function foo() {  // The second function declaration is moved to top
        return 2;
    }
    function outerFunction() {
        return foo();
    }
    alert(outerFunction()); //So executing from top to bottom,
                            //the last foo() returns 2 which gets displayed

    関数式を呼び出す前に定義しないと、エラーになります。また、ここでは関数定義自体は先頭に移動されず、関数宣言のようにメモリにコミットされません。しかし、関数を割り当てる変数は巻き上げられ、未定義はそれに割り当てられます。

    関数式を使用した同じ関数:

    function outerFunction() {
        var foo = function() {
           return 1;
        }
        return foo();
        var foo = function() {
           return 2;
        }
    }
    alert(outerFunction()); // Displays 1

    これは、実行中は次のようになるためです。

    function outerFunction() {
       var foo = undefined;
       var foo = undefined;
    
       foo = function() {
          return 1;
       };
       return foo ();
       foo = function() {   // This function expression is not reachable
          return 2;
       };
    }
    alert(outerFunction()); // Displays 1
  2. のような非機能ブロック内の関数宣言を書くことは安全ではない場合、彼らはアクセスできなくなりますので。

    if (test) {
        function x() { doSomething(); }
    }
  3. 以下のような名前付き関数式は、バージョン9より前のInternet Explorerブラウザーでは機能しない場合があります。

    var today = function today() {return new Date()}

1
@Arjun何年も前に質問された場合の問題は何ですか?質問はいつ尋ねられても、答えはOPだけでなく、潜在的にすべてのSOユーザーに利益をもたらす。そして、すでに受け入れられている答えがある質問に答えることの何が問題になっていますか?
SantiBailors 2015年

1
あなたが古い質問に答えることを理解するようになった@アルジュンは悪くありません。もしそうなら、SOはそのような障壁を持っていただろう。(この質問のコンテキストではありませんが)APIに変更があり、誰かがそれを見つけて新しいAPIで回答を提供すると想像してください。許可されるべきではありませんか?それまでは、答えが意味をなさず、ここに属さない限り、自動的に反対投票されて削除されます。気にする必要はありません!!!!
Sudhansu Choudhary

15

これらの関数を使用してオブジェクトを作成すると、次のようになります。

var objectOne = new functionOne();
console.log(objectOne.__proto__); // prints "Object {}" because constructor is an anonymous function

var objectTwo = new functionTwo();
console.log(objectTwo.__proto__); // prints "functionTwo {}" because constructor is a named function

これは再現できないようです。console.log(objectOne.__proto__);コンソールに「functionOne {}」を出力します。これが事実である理由について何か考えはありますか?
Mike

私もそれを再現することができないようです。
daremkd

1
これはデバッガーの機能(ログに記録されたオブジェクトの「クラス」を表示する機能)であり、ほとんどのデバッガーは、最近の無名関数式でも名前を導出できます。ところで、2つのインスタンスの間に機能的な違いがないことを明確にする必要があります。
Bergi、2016年

12

「名前付き関数はスタックトレースに表示される」という引数に照らして、最近のJavaScriptエンジンは、実際には匿名関数を表すことが非常に可能です。

これを書いている時点では、V8、SpiderMonkey、Chakra、Nitroは常に名前付き関数を名前で参照しています。ほとんどの場合、匿名関数が存在する場合、その識別子によって匿名関数を参照します。

SpiderMonkeyは、別の関数から返された無名関数の名前を把握できます。残りはできません。

イテレータと成功のコールバックをトレースに表示したい場合は、これらにも名前を付けることができます...

[].forEach(function iterator() {});

しかし、ほとんどの場合、強調する価値はありません。

ハーネス(フィドル

'use strict';

var a = function () {
    throw new Error();
},
    b = function b() {
        throw new Error();
    },
    c = function d() {
        throw new Error();
    },
    e = {
        f: a,
        g: b,
        h: c,
        i: function () {
            throw new Error();
        },
        j: function j() {
            throw new Error();
        },
        k: function l() {
            throw new Error();
        }
    },
    m = (function () {
        return function () {
            throw new Error();
        };
    }()),
    n = (function () {
        return function n() {
            throw new Error();
        };
    }()),
    o = (function () {
        return function p() {
            throw new Error();
        };
    }());

console.log([a, b, c].concat(Object.keys(e).reduce(function (values, key) {
    return values.concat(e[key]);
}, [])).concat([m, n, o]).reduce(function (logs, func) {

    try {
        func();
    } catch (error) {
        return logs.concat('func.name: ' + func.name + '\n' +
                           'Trace:\n' +
                           error.stack);
        // Need to manually log the error object in Nitro.
    }

}, []).join('\n\n'));

V8

func.name: 
Trace:
Error
    at a (http://localhost:8000/test.js:4:11)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: b
Trace:
Error
    at b (http://localhost:8000/test.js:7:15)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: d
Trace:
Error
    at d (http://localhost:8000/test.js:10:15)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: 
Trace:
Error
    at a (http://localhost:8000/test.js:4:11)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: b
Trace:
Error
    at b (http://localhost:8000/test.js:7:15)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: d
Trace:
Error
    at d (http://localhost:8000/test.js:10:15)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: 
Trace:
Error
    at e.i (http://localhost:8000/test.js:17:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: j
Trace:
Error
    at j (http://localhost:8000/test.js:20:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: l
Trace:
Error
    at l (http://localhost:8000/test.js:23:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: 
Trace:
Error
    at http://localhost:8000/test.js:28:19
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: n
Trace:
Error
    at n (http://localhost:8000/test.js:33:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: p
Trace:
Error
    at p (http://localhost:8000/test.js:38:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27 test.js:42

クモザル

func.name: 
Trace:
a@http://localhost:8000/test.js:4:5
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: b
Trace:
b@http://localhost:8000/test.js:7:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: d
Trace:
d@http://localhost:8000/test.js:10:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: 
Trace:
a@http://localhost:8000/test.js:4:5
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: b
Trace:
b@http://localhost:8000/test.js:7:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: d
Trace:
d@http://localhost:8000/test.js:10:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: 
Trace:
e.i@http://localhost:8000/test.js:17:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: j
Trace:
j@http://localhost:8000/test.js:20:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: l
Trace:
l@http://localhost:8000/test.js:23:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: 
Trace:
m</<@http://localhost:8000/test.js:28:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: n
Trace:
n@http://localhost:8000/test.js:33:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: p
Trace:
p@http://localhost:8000/test.js:38:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1

チャクラ

func.name: undefined
Trace:
Error
   at a (http://localhost:8000/test.js:4:5)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at b (http://localhost:8000/test.js:7:9)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at d (http://localhost:8000/test.js:10:9)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at a (http://localhost:8000/test.js:4:5)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at b (http://localhost:8000/test.js:7:9)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at d (http://localhost:8000/test.js:10:9)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at e.i (http://localhost:8000/test.js:17:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at j (http://localhost:8000/test.js:20:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at l (http://localhost:8000/test.js:23:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at Anonymous function (http://localhost:8000/test.js:28:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at n (http://localhost:8000/test.js:33:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at p (http://localhost:8000/test.js:38:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)

ニトロ

func.name: 
Trace:
a@http://localhost:8000/test.js:4:22
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: b
Trace:
b@http://localhost:8000/test.js:7:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: d
Trace:
d@http://localhost:8000/test.js:10:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: 
Trace:
a@http://localhost:8000/test.js:4:22
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: b
Trace:
b@http://localhost:8000/test.js:7:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: d
Trace:
d@http://localhost:8000/test.js:10:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: 
Trace:
i@http://localhost:8000/test.js:17:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: j
Trace:
j@http://localhost:8000/test.js:20:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: l
Trace:
l@http://localhost:8000/test.js:23:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: 
Trace:
http://localhost:8000/test.js:28:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: n
Trace:
n@http://localhost:8000/test.js:33:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: p
Trace:
p@http://localhost:8000/test.js:38:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

12

JavaScriptでは、関数を作成する2つの方法があります。

  1. 関数宣言:

    function fn(){
      console.log("Hello");
    }
    fn();

    これは非常に基本的で自明であり、多くの言語で使用され、Cファミリの言語全体で標準となっています。それを定義した関数を宣言し、呼び出して実行しました。

    知っておくべきことは、関数は実際にはJavaScriptのオブジェクトであることです。内部的に、上記の関数のオブジェクトを作成し、fnという名前を付けるか、オブジェクトへの参照をfnに格納します。関数はJavaScriptのオブジェクトです。関数のインスタンスは、実際にはオブジェクトインスタンスです。

  2. 関数式:

    var fn=function(){
      console.log("Hello");
    }
    fn();

    JavaScriptにはファーストクラスの関数があります。つまり、文字列または数値を作成して変数に割り当てるのと同じように、関数を作成して変数に割り当てます。ここでは、fn変数が関数に割り当てられています。この概念の理由は、関数がJavaScriptのオブジェクトであることです。fnは、上記の関数のオブジェクトインスタンスを指しています。関数を初期化し、変数に割り当てました。関数を実行して結果を割り当てるのではありません。

リファレンス:JavaScript関数宣言構文:var fn = function(){} vs関数fn(){}


1
3番目のオプションはvar fn = function fn() {...}どうですか?
chharvey 2016

こんにちはChharvey、質問については不明ですが、すでに述べた関数式について話していると思います。ただし、それでも混乱がある場合は、さらに複雑になります。
Anoop Rai

はい、私は名前付き関数式について尋ねていました。これはオプション#2に似ていますが、関数に識別子がある点が異なります。通常、この識別子は割り当て先の変数と同じですが、常にそうであるとは限りません。
chharvey

1
はい名前付き関数式は私のオプション#2に似ています。識別子は使用されないため、識別子を持つことは必須ではありません。関数式を実行するときはいつでも、関数オブジェクトを保持する変数を使用します。識別子は目的を果たしません。
Anoop Rai

11

どちらも関数を定義する方法が異なります。違いは、ブラウザがそれらを解釈して実行コンテキストにロードする方法です。

最初のケースは、インタプリタがそのコード行に到達したときにのみロードされる関数式です。したがって、次のようにすると、functionOneがfunctionではないというエラーが発生します

functionOne();
var functionOne = function() {
    // Some code
};

その理由は、最初の行ではfunctionOneに値が割り当てられていないため、未定義だからです。これを関数として呼び出そうとしているため、エラーが発生しています。

2行目では、無名関数の参照をfunctionOneに割り当てています。

2番目のケースは、コードが実行される前にロードされる関数宣言です。したがって、次のようにすると、コードの実行前に宣言が読み込まれるため、エラーが発生しません。

functionOne();
function functionOne() {
   // Some code
}

11

パフォーマンスについて:

の新しいバージョンでV8は、いくつかの内部最適化が導入されましたSpiderMonkey

現在、表現と宣言の違いはほとんどありません。
関数式が高速になりました。

Chrome 62.0.3202 クロムテスト

FireFox 55 Firefoxテスト

Chrome Canary 63.0.3225 Chrome Canaryテスト


Anonymous関数式は 、関数式に対してパフォーマンスが優れているようですNamed


Firefox Chrome Canary ChromeFirefox named_anonymous Chromeカナリアnamed_anonymous Chrome named_anonymous


1
はい、この違いはあまり重要ではないので、開発者は、どちらがより高速であるではなく、特定のニーズに対してより保守しやすいアプローチに関心を持つこと期待されます(ブラウザーの動作に応じて、試行ごとに異なるjsperf結果が得られます- JavaScriptタスクの大部分は、この程度のマイクロ最適化に関与する必要はありません)。
squidbe

@squidbe違いはありません。ここを見てください:jsperf.com/empty-tests-performance
Jack Giffin

10

それらはいくつかの小さな違いはありますが、1つ目は匿名関数(関数宣言)に割り当てられた変数で、2つ目はJavaScriptで関数を作成する通常の方法(匿名関数宣言)です。どちらにも使用方法、短所、長所があります。 :

1.関数式

var functionOne = function() {
    // Some code
};

関数式は、関数をより大きな式構文(通常は変数の割り当て)の一部として定義します。関数式で定義された関数には、名前を付けることも匿名にすることもできます。関数式は、「関数」で始めることはできません(したがって、以下の自己呼び出しの例の周りの括弧)。

JavaScriptの関数がホイストできることを知っているように、関数に変数を割り当てます。これはホイストがないことを意味します。宣言する前に呼び出すことができますが、変数にアクセスする前に変数を宣言する必要があるため、この場合はできません。それが宣言されている場所の前に関数にアクセスします。また、それはあなたがあなたの関数を書く方法であるかもしれません、別の関数を返す関数のために、この種の宣言は理にかなっているかもしれません、ECMA6以上ではこれを矢印関数に割り当てることができます匿名関数の呼び出しに使用できます。この宣言方法は、JavaScriptでコンストラクター関数を作成するためのより良い方法です。

2.関数宣言

function functionTwo() {
    // Some code
}

関数宣言は、変数の割り当てを必要とせずに名前付き関数変数を定義します。関数宣言はスタンドアロン構造として発生し、非関数ブロック内にネストすることはできません。それらを変数宣言の兄弟と考えると役に立ちます。変数宣言が「var」で始まる必要があるのと同様に、関数宣言は「function」で始まる必要があります。

これはJavaScriptで関数を呼び出す通常の方法です。JavaScriptですべての関数がホイストされるように、宣言する前にこの関数を呼び出すことができますが、「厳密な使用」を行うと、期待どおりにホイストされません。これは良い方法です。行が大きくなく、コンストラクタ関数でもない通常のすべての関数を呼び出す。

また、JavaScriptでの巻き上げの動作に関する詳細情報が必要な場合は、以下のリンクにアクセスしてください。

https://developer.mozilla.org/en-US/docs/Glossary/Hoisting


1
...also this way of declaring is a better way to create Constructor functions in JavaScript、詳しく説明してもらえますか、興味があります。
カールモリソン

1つの理由は、この関数Number(){[ネイティブコード]}のように作成されたJavaScriptのすべての組み込みコンストラクター関数であり、組み込み関数と混同しないでください。この場合も後で参照する方が安全であり、終了します。きちんとしたコードを上げますが、巻き上げを使用しません...
Alireza

8

これは、関数を宣言する2つの可能な方法にすぎません。2番目の方法では、宣言の前に関数を使用できます。


7

new Function()関数の本体を文字列で渡すために使用できます。したがって、これを使用して動的関数を作成できます。また、スクリプトを実行せずにスクリプトを渡します。

var func = new Function("x", "y", "return x*y;");
function secondFunction(){
   var result;
   result = func(10,20);
   console.log ( result );
}

secondFunction()

これは良いことであり、真実ですが、これだけでは、求められるクエシトンとどの程度正確に関連していますか?
ジャック
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.