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

2008-12-03 javascript function syntax idioms

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

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

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

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

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

Answers

違いは、 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

まず、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;

この場合、 xyzabcは同じオブジェクトのエイリアスです。

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);
  // ...
}

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

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

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()場合は、コンテキストに依存し、それが実際に定義されている場所を推測する可能性があります。答えは次のとおりです。それはブラウザーに依存します。

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

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

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

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

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

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

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

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

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

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

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

その他の技術情報

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

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

グローバルコンテキストについて言えば、最後にvarステートメントとFunctionDeclarationを使用すると、グローバルオブジェクトに削除不可のプロパティが作成されますが、両方の値を上書きできます

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ステートメントを使用せずに割り当てを使用すると、参照された識別子がスコープチェーンで見つからない場合、グローバルオブジェクトの削除可能なプロパティになります。

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

必読:

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

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

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

}

または

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

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

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

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

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

(function(){
    var exports = {};

    function privateUtil() {
            ...
    }

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

    return exports;
})();

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

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

@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." );

他の回答で言及されていないもう1つの違いは、無名関数を使用すると

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を使用して、コンストラクタの名前を文字列として取得できます。

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

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

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

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

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

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

最初のもの(関数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)は構文を次のように定義します
function Identifier opt (FormalParameterList opt ){FunctionBody}

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

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

基本的に、すべての関数宣言と変数宣言は、宣言が発生する関数の一番上に引き上げられます(これが、JavaScriptに関数スコープがあると言う理由です)。

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

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

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

いくつかの例...

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

変数fooは関数の先頭に巻き上げられ、 undefinedに初期化されるため、 !footrueなので、 foo10が割り当てられます。 barのスコープ外のfooは何の役割も果たさず、そのままです。

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

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

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

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

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

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

、このmyfunction定義は、解析時に行われるため、以前の定義をオーバーライドします。

ながら

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

conditionが満たされた場合にのみmyfunctionを定義する正しいジョブをmyfunctionます。

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

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

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

条項:

クイックリスト:

  • 関数宣言

  • 「匿名」 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まで、仕様は、 tryifswitchwhileなどの制御構造内に次のように関数宣言を配置した場合のJavaScriptエンジンの動作をカバーしていませんでした。

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を検索して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"

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

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

矢印関数式(ES2015 +)

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

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

n => n * 2map()呼び出しに隠れていることを確認してください。それは関数です。

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

  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番目の引数としてインデックスを渡すことにArray#mapください。)

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

単一の式だけではない場合は、通常どおり、 {}と明示的な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));

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

オブジェクト初期化子でのメソッド宣言(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ソースオブジェクトの一つとしては)、メソッドの構文は、その意味するかもしれませんオブジェクトはガベージコレクションされた可能性がある場合にメモリに保持されます(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という名前を取得するコンストラクタ用、もう1つはgetFullNameに割り当てられた関数であるPerson.prototypeです。

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

function abc(){}

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

var abc = function() {};

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

簡単に言えば:

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

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

このトピックについてさらに学習するには、これを強くお勧めします リンク

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

functionTwo();
function functionTwo() {
}

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

なぜなら:

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

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

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

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

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

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

「名前付き関数はスタックトレースに表示される」という引数に照らして、最新の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:
[email protected]://localhost:8000/test.js:4:5
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


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


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


func.name: 
Trace:
[email protected]://localhost:8000/test.js:4:5
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


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


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


func.name: 
Trace:
[email protected]://localhost:8000/test.js:17:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


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


func.name: l
Trace:
[email protected]://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:
[email protected]://localhost:8000/test.js:33:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: p
Trace:
[email protected]://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:
[email protected]://localhost:8000/test.js:4:22
http://localhost:8000/test.js:47:13
[email protected][native code]
global [email protected]://localhost:8000/test.js:44:33

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

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

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

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

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

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

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

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

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

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

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

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

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

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

関数宣言の方が高速であり、それがWeb開発で本当に重要なことですよね? ;)

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

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

関数式:

var foo = function foo() {};

関数ステートメント:

function foo() {};

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

そう

function foo() {};

に拡大する

var foo = function foo() {};

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

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

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

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

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

  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のような非機能ブロック内の関数宣言を書くことは安全ではありません。

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

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

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

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

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

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

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

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

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

ホイスト は、すべての変数と関数の宣言を現在のスコープの最上部に移動する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

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

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

secondFunction()

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つ目は匿名関数(関数宣言)に割り当てられた変数で、2つ目はJavaScriptで関数を作成する通常の方法(匿名関数宣言)です。どちらにも使用方法、短所、長所があります。 :

1.関数式

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

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

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

2.関数宣言

function functionTwo() {
    // Some code
}

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

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

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

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

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

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

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

Chrome 62.0.3202 クロムテスト

FireFox 55 Firefoxテスト

Chrome Canary 63.0.3225 Chrome Canaryテスト


Anonymous関数式のパフォーマンスが向上しているようです Named関数式に対して。


Firefox Firefox named_anonymous Chrome Canary Chromeカナリアnamed_anonymous クロム Chrome named_anonymous

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

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

function add()スコープが最も近いブロックであるため、次のように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;
}

上記のコードの機能は、以下のコードと同じです。 var add;だけなので、明示的にadd = undefined割り当てる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()スーパーシードvar 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(){}の名前はthefuncnameです。

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()したArrow 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;
    }
})();

私は関数を変数として定義することを好みます:

let first = function(x){
   return x[0];
}

の代わりに:

function first(){
    ....
}

関数を定義するときに式とデコレーターを使用できるからです。例えば:

let safe = function(f){
  try {f()...}
}
let last = safe(function(x){return x[0]}).

また、ES6の場合ははるかに短くなります。

 let last = x => x[0]
 ...........
 function last(x){
     return x[0];
 }
......

let last = safe(x => x[0]);

JSの式 :値を返すもの
例:Chromeコンソールで以下を試してください:

a = 10
output : 10

(1 + 3)
output = 4

宣言/ステートメント :値を返さないもの
例:

if (1 > 2) {
 // do something. 
}

ここ(1> 2)は式ですが、 'if'文はそうではありません。何も返さない。


同様に、関数宣言/ステートメントと関数式があります。
例を見てみましょう:

// test.js

var a = 10;

// function expression
var fun_expression = function() {
   console.log("Running function Expression");
}

// funciton expression

function fun_declaration() {
   console.log("Running function Statement");
}

重要: JavaScriptエンジンが上記のjsファイルを実行するとどうなりますか。

  • このjsが実行されると、次のことが起こります。

    1. メモリには変数「a」と「fun_expression」が作成されます。そして、関数ステートメント 'fun_declaration'のメモリが作成されます
    2. 「a」には「未定義」が割り当てられます。 「fun_expression」には「undefined」が割り当てられます。 'fun_declaration'は完全にメモリ内にあります。
      注:上記のステップ1および2は、「実行コンテキスト-作成フェーズ」と呼ばれます。

次に、jsを更新するとします。

// test.js

console.log(a)  //output: udefined (No error)
console.log(fun_expression)  // output: undefined (No error)
console.log(fun_expression()) // output: Error. As we trying to invoke undefined. 
console.log(fun_declaration()) // output: running function statement  (As fun_declaration is already hoisted in the memory). 

var a = 10;

// function expression
var fun_expression = function() {
   console.log('Running function expression')
}

// function declaration

function fun_declaration() {
   console.log('running function declaration')
}

console.log(a)   // output: 10
console.log(fun_expression()) //output: Running function expression
console.log(fun_declaration()) //output: running function declaration

コメントで前述した出力は、関数式と関数ステートメント/宣言の違いを理解するのに役立ちます。

これは関数式と呼ばれます。

var getRectArea = function(width, height) {
    return width * height;
};

console.log("Area of Rectangle: " + getRectArea(3,4));
// This should return the following result in the console: 
// Area of Rectangle: 12

これは関数宣言と呼ばれます。

var w = 5;
var h = 6;

function RectArea(width, height) {  //declaring the function
  return area = width * height;
}                                   //note you do not need ; after }

RectArea(w,h);                      //calling or executing the function
console.log("Area of Rectangle: " + area);
// This should return the following result in the console: 
// Area of Rectangle: 30

これが関数式と関数宣言の違いとその使い方の説明に役立つことを願っています。ありがとう。

注意すべき重要な点の1つは、次のとおりです。

2つの機能があります:-

sum(1,2);

const sum = function(first, second) {
  return first + second;
}

上記の場合、合計が定義されていないというエラーが発生しますが、

sum(1,2);

function sum(first, second) {
  return first + second;
}

この場合、 巻き上げが行われるため、この機能はエラーになりません。

1つ目は無名関数式で、2つ目は関数宣言です。無名関数には名前がありません。無名関数式と関数ステートメントの主な違いは、関数名です。

名前付き関数と匿名関数

無名関数はすばやく簡単に入力でき、多くのライブラリとツールはこの慣用的なコードスタイルを奨励する傾向があります。ただし、無名関数にはいくつかの欠点があります。

  • 読みやすさ:匿名関数では名前が省略されるため、コードが読みにくくなる可能性があります。

  • デバッグ:匿名関数はスタックトレースで有用な名前を持たないため、デバッグがより困難になる可能性があります。

  • 自己参照:関数が再帰などのためにそれ自体を参照する必要がある場合はどうなりますか?

関数式の命名:

関数式に名前を付けると、これらすべての欠点に非常に効果的に対処でき、明らかな欠点はありません。ベストプラクティスは、常に関数式に名前を付けることです。

setTimeout(function timeHandler() { // <-- look, a name here!
  console.log("I've waited 1 second");
}, 1000);

IIFEの命名(即時に呼び出される関数式)

(function IIFE(str) { // <-- look, always name IIFEs!
  console.log(str); // "Hello!"
})('Hello!');

変数に割り当てられた関数の場合、この場合、関数に名前を付けることはあまり一般的ではなく、混乱を引き起こす可能性があります。この場合、矢印関数の方が適しています。

両方の関数のもう1つの違いは、functionOneが内部で複数の関数を保持できる変数として使用でき、functionTwoが呼び出されたときにすべて実行されるコードのブロックを保持できることです。以下を確認してください:

   var functionOne = (function() {
      return {

         sayHello: function(){
                console.log('say hello')

         },
         redirectPage:function(_url){
                window.location.href = _url;
         }

      }
})();

呼び出す関数を選択できます。例:functionOne.sayHelloまたはfunctionOne。 redirectPage。そしてfunctionTwoを呼び出すと、コードのブロック全体が実行されます。

関数宣言と式の間の巻き上げ動作は重要ですが、この2つには別の違いがあります。

条件ステートメント内の関数

MDNが推奨する一般的な方法は、 ifステートメント内の宣言ではなく関数式を使用することです。リンクの例から、 ifステートメント内の関数宣言はChromeとFirefoxで同じように動作しますが、Safariでは動作しません

免責事項:macOSはMicrosoft Edgeを実行できないため、検証できません。

// function declaration example

var hoisted = "foo" in this;

console.log(`'foo' name ${hoisted ? "is" : "is not"} hoisted. typeof foo is ${typeof foo}`);

if (false) {
  function foo() {
    return 1;
  }
}

// In Chrome: 
// 'foo' name is hoisted. typeof foo is undefined

// In Firefox:
// 'foo' name is hoisted. typeof foo is undefined

// In Safari:
// 'foo' name is hoisted. typeof foo is function

fooを関数式に変換すると、3つのWebブラウザーすべてで一貫した結果が得られます。

// function expression example

var hoisted = "foo" in this;

console.log(`'foo' name ${hoisted ? "is" : "is not"} hoisted. typeof foo is ${typeof foo}`);

if (false) {
  var foo = function() {
    return 1;
  }
}

// In Chrome: 
// 'foo' name is hoisted. typeof foo is undefined

// In Firefox:
// 'foo' name is hoisted. typeof foo is undefined

// In Safari:
// 'foo' name is hoisted. typeof foo is undefined

差分関数宣言と関数式:

Javascriptにはファーストクラスの機能があります。つまり、他の変数と同じように扱うことができます。関数は、関数の引数として渡したり、関数から返したり、変数に格納したりできます

ただし、変数に関数を格納する( 関数式 )だけが関数を作成する方法ではありません。これは、 関数宣言を介して行うこともできます。主な違いは次のとおりです。

  1. 関数式は匿名にすることができますが、関数宣言には名前が必要です。
  2. どちらにも、関数を識別するために使用される名前プロパティがあります。関数式の名前プロパティは、それがバインドされている変数の名前ですが、関数宣言の名前は単に指定された名前です。
  3. 関数宣言はホイストされますが、関数式はホイストされません。変数だけがundefinedの値を持つように巻き上げられます。

次に例を示します。

try {
  functionOne();
} catch (e) {
  console.log('i cant run because im not hoisted');
}

functionTwo();

// function expression, does not get hoisted
let functionOne = function randomName() {
    // Some code
};

// function declaration, gets hoisted
function functionTwo() {
   console.log('I get hoisted');
}

try {
  randomName(); // this isn't the proper name, it is functionOne
} catch (e) {
  console.log('You cant call me with randomName my name is function one');
}

var functionOne = function() {}は実行時に定義し、 function functionTwo() {}は解析時に定義します。

// Run-Time function declaration 
functionOne(); // Calling functionOne function here will give an Error
var functionOne = function () {
  // Some code
};

// Parse-Time function declaration 
functionTwo(); // Calling functionTwo function will not give an Error
function functionTwo() {
  // Some code...
}

実行時間と解析時間の間の説明 JavaScriptランタイムと解析時間

Related