7366

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

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

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

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

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

4

41 に答える 41

5414

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

たとえば、関数式:

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

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

そして、関数宣言:

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

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

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

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

于 2008-12-03T11:37:42.267 に答える
2048

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

上記の例では、外部名を使用して同じことを行うことができますが、扱いにくく (そして遅く) なります。

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

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

function abc(){}

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

// 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()、答えは次のとおりです。ブラウザに依存します。

于 2008-12-03T17:43:35.180 に答える
706

関数を作成する標準フォームの概要は次のとおりです。

条項:

クイック リスト:

  • 関数宣言

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

  • 名前付きfunction

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

  • アロー関数式 (ES2015+) (無名関数式と同様に、明示的な名前を必要とせず、名前付きの関数を作成できます)

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

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

関数宣言

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

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

関数宣言は宣言です。それは声明や表現ではありません。そのため、a を続けません;(ただし、無害です)。

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

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

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

tryES2015 まで、次のようにifswitchwhile、 などの制御構造内に関数宣言を配置した場合に JavaScript エンジンが何をすべきかについて、仕様はカバーしていませんでした。

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

また、それらは段階的なコードが実行される前に処理されるため、制御構造内にあるときに何をすべきかを理解するのは困難です。

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

ES2015 の時点で、仕様は何をすべきかを示しています。実際、それは3つの別々のことをすることを与えます:

  1. Web ブラウザーではないルース モードの場合、JavaScript エンジンは 1 つのことを行うことになっています。
  2. Web ブラウザーでルース モードの場合、JavaScript エンジンは別の処理を行うことになっています。
  3. 厳密モード (ブラウザーかどうかに関係なく) の場合、JavaScript エンジンはさらに別のことを行うことになっています。

Loose モードのルールはトリッキーですが、strictモードでは、ブロック内の関数宣言は簡単です: それらはブロックに対してローカルであり (ブロック スコープがあり、これも 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"

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

Object.definePropertyObject.defineProperties、およびあまり知られていない の 2 番目の引数を使用してアクセサ関数を作成することもできますObject.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は も閉じます。) これは、それらの内部の がそれらが作成された場所と同じであり、変更できないことを意味します。argumentssuperthisthis

  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)。

複数の式を実行している場合は、通常どおり{}and を明示的に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もう 1 つはgetFullNameに割り当てられた関数である用Person.prototypeです。

于 2014-03-04T13:35:48.803 に答える
164

グローバル コンテキストについて言えば、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 ModeReferenceErrorの下で ECMAScript 5 にスローします。

必読:

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

于 2010-08-08T19:32:11.603 に答える
138

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

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

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

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

より多くの技術情報

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

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

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

私はよく、次のようなパターンで JavaScript モジュールを構築します。

(function(){
    var exports = {};

    function privateUtil() {
            ...
    }

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

    return exports;
})();

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

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

于 2011-03-03T19:19:18.113 に答える
84

2 番目の方法よりも 1 番目の方法を優先する場合の例は、関数の以前の定義をオーバーライドすることを避ける必要がある場合です。

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

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

その間

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

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

于 2013-03-29T13:26:24.443 に答える
69

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

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

}

また

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

名前空間には多くの手法があります。大量の JavaScript モジュールが利用できるようになったことで、これはますます重要になっています。

また、JavaScript で名前空間を宣言するにはどうすればよいですか?も参照してください。

于 2010-08-08T19:44:40.373 に答える
62

巻き上げ は、すべての変数と関数の宣言を現在のスコープの先頭に移動する 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';
}
  • ページ内で宣言された関数は、グローバル アクセスを持つページの上部に引き上げられます。
  • 関数ブロック内で宣言された関数は、ブロックの先頭に持ち上げられます。
  • 関数のデフォルトの戻り値は「未定義」、変数宣言のデフォルト値も「未定義」

    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 でアロー関数が導入されまし: アロー関数式は構文が短く、メソッド以外の関数に最適であり、コンストラクターとして使用することはできません。

ArrowFunction : ArrowParameters => ConciseBody.

const fn = (item) => { return item & 1 ? 'Odd' : 'Even'; };
console.log( fn(2) ); // Even
console.log( fn(3) ); // Odd
于 2016-01-25T14:46:53.683 に答える
48

他の人が巻き上げ部分を徹底的にカバーしたという理由だけで、私は自分の答えを追加しています。

私は長い間どちらの方法が良いのか疑問に思っていました.http://jsperf.comのおかげで今ではわかりました:)

ここに画像の説明を入力

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

于 2015-05-01T15:06:55.083 に答える
38

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

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

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

  • 関数宣言が巻き上げられると、関数本体が「追従」するため、関数本体が評価されると、変数はすぐに関数オブジェクトにバインドされます。

  • 変数宣言が巻き上げられると、初期化は続きませ が、「取り残されます」。変数は undefined関数本体の開始時に初期化され 、コード内の元の場所に値が割り当てられます。(実際には、同じ名前の変数の宣言が発生するすべての場所で値が割り当てられます。)

ホイストの順序も重要です。関数宣言は同じ名前の変数宣言よりも優先され、最後の関数宣言は同じ名前の前の関数宣言よりも優先されます。

いくつかの例...

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

変数fooは関数の先頭に持ち上げられ、 に初期化されるためundefined、つまり!fooが割り当てられます。のスコープの外側は何の役割も果たさず、そのままです。truefoo10foobar

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

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

于 2013-02-06T16:29:20.620 に答える
36

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

function abc(){}

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

var abc = function() {};

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

簡単に言えば:

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

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

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

于 2014-06-05T08:28:44.063 に答える
34

コードの保守コストに関しては、名前付き関数の方が望ましいです。

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

名前付き関数の長所がさらに続くと思います。そして、名前付き関数の利点としてリストされているものは、匿名関数の欠点です。

歴史的に、名前付き関数を持つメンバーをリストする言語としての JavaScript の無能さから、無名関数が登場しました。

{
    member:function() { /* How do I make "this.member" a named function? */
    }
}
于 2010-01-23T20:32:54.490 に答える
31

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

無名関数とラムダ計算の詳細については、ウィキペディアから始めるとよいでしょう: Anonymous Functions .

于 2008-12-18T19:30:42.867 に答える
29

私は非常に具体的な理由でコードで変数アプローチを使用しています。その理論は上記で抽象的な方法で説明されていますが、例は、JavaScript の専門知識が限られている私のような一部の人々に役立つかもしれません。

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

ブランディングには、特定の機能を必要とするものと、必要としないものがあります。新しいブランディング固有のことを行うために、新しい関数を追加する必要がある場合があります。共有コードを変更できてよかったのですが、160 セットのブランディング ファイルすべてを変更する必要はありません。

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

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

上記の人々のコメントから、静的関数も再定義できる可能性があると思いますが、変数の解決策は素晴らしく明確だと思います。

于 2012-11-29T11:28:49.407 に答える
18

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

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

于 2012-10-15T10:42:47.903 に答える
17

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

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

関数宣言関数式が何であるかを知りたいと思うかもしれません。

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

function foo() {
    return 3;
}

ECMA 5 (13.0) は、構文を
function Identifier ( 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 }として定義しています。

于 2013-01-05T18:37:52.810 に答える
12

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

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

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

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

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

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

functionOne();
function functionOne() {
   // Some code
}
于 2015-12-28T20:18:03.510 に答える
8

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

于 2015-06-24T10:08:07.703 に答える
7

最初の関数構文は無名関数式です:

var functionOne = function() {
  // do something...
};

一方、2 つ目はFunction Declarationです。

function functionTwo () {
  // do something...
}

匿名関数には呼び出す名前がないため、両者の主な違いは関数名です。

名前付き関数対。無名関数

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

  • 可読性:無名関数は名前を省略しているため、コードが読みにくくなる可能性があります。

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

  • 自己参照:再帰などのために、関数がそれ自体を参照する必要がある場合。

ネーミング関数式

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

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

IIFE (Immediate Invoked Function Expression) の命名

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

変数に割り当てられた関数の場合、この場合、関数に名前を付けることはあまり一般的ではなく、混乱を招く可能性があります。この場合、アロー関数がより適切な選択である可能性があります。

于 2018-12-17T16:58:55.433 に答える
5

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

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

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

于 2018-07-14T22:56:13.837 に答える
2

差分関数宣言と関数式:

Javascript には第一級の機能があります。これは、他の変数と同じように扱うことができることを意味します。関数は、関数内で引数として渡したり、関数から返したり、変数に格納したりできます

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

  1. 関数式は匿名にすることができますが、関数宣言には名前が必要です。
  2. どちらにも、関数を識別するために使用される name プロパティがあります。関数式の name プロパティは、それがバインドされている変数の名前ですが、関数宣言の名前は単に与えられた名前です。
  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');
}

:

于 2019-06-20T10:06:36.403 に答える
1

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

   var functionOne = (function() {
      return {

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

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

      }
})();

どの関数を呼び出すかを選択できます。例: functionOne.sayHello または functionOne. リダイレクト ページ。functionTwo を呼び出すと、コードのブロック全体が実行されます。

于 2019-01-02T06:50:44.900 に答える
1

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 実行時と解析時の説明

于 2020-03-03T08:43:56.080 に答える