394

概要

JavaScriptでカプセル化された無名関数の構文の背後にある理由を説明できますか?なぜこれが機能するのですか:(function(){})();しかし、これは機能しません:function(){}();


私が知っていること

JavaScriptでは、次のような名前付き関数を作成します。

function twoPlusTwo(){
    alert(2 + 2);
}
twoPlusTwo();

匿名関数を作成して変数に割り当てることもできます。

var twoPlusTwo = function(){
    alert(2 + 2);
};
twoPlusTwo();

匿名関数を作成し、それを角かっこで囲んですぐに実行することで、コードのブロックをカプセル化できます。

(function(){
    alert(2 + 2);
})();

これは、モジュール化されたスクリプトを作成するときに、Greasemonkeyスクリプト、jQueryプラグインなどの場合のように、現在のスコープまたはグローバルスコープが競合する可能性のある変数で乱雑になるのを防ぐのに役立ちます。

今、私はこれが機能する理由を理解しています。角かっこは内容を囲み、結果のみを表示します(これを説明するためのより良い方法があると確信しています)(2 + 2) === 4


わからないこと

しかし、なぜこれが同じように機能しないのか理解できません。

function(){
    alert(2 + 2);
}();

それを説明してもらえますか?

4

10 に答える 10

429

FunctionDeclarationとして解析されており、関数宣言の名前識別子が必須であるため、機能しません。

括弧で囲むと として評価され、FunctionExpression関数式に名前を付けても付けなくてもかまいません。

a の文法はFunctionDeclaration次のようになります。

function Identifier ( FormalParameterListopt ) { FunctionBody }

そしてFunctionExpression

function Identifieropt ( FormalParameterListopt ) { FunctionBody }

ご覧のとおり、Identifier(Identifier opt ) トークンFunctionExpressionはオプションであるため、名前を定義せずに関数式を使用できます。

(function () {
    alert(2 + 2);
}());

または名前付き関数式:

(function foo() {
    alert(2 + 2);
}());

括弧 (正式には Grouping Operatorと呼ばれます) は式のみを囲むことができ、関数式が評価されます。

2 つの文法生成はあいまいになる可能性があり、まったく同じに見える可能性があります。次に例を示します。

function foo () {} // FunctionDeclaration

0,function foo () {} // FunctionExpression

パーサーは、出現するコンテキストに応じて、それがFunctionDeclarationかかを認識します。FunctionExpression

上記の例では、コンマ演算子も式のみを処理できるため、2 番目の例は式です。

一方、 s は実際には " " コードと呼ばれるもの、つまりグローバル スコープの外側のコード、および他の関数の内側FunctionDeclarationにのみ現れる可能性があります。ProgramFunctionBody

ブロック内の関数は、予測できない動作を引き起こす可能性があるため、避ける必要があります。

if (true) {
  function foo() {
    alert('true');
  }
} else {
  function foo() {
    alert('false!');
  }
}

foo(); // true? false? why?

上記のコードは、実際には a を生成する必要がありますSyntaxError。これは、 a にBlockはステートメントのみを含めることができる (そして、ECMAScript 仕様では関数ステートメントを定義していないため) ため'false!'です。

Mozilla の実装 (Rhino、SpiderMonkey) は、異なる動作をします。それらの文法には非標準のFunction ステートメントが含まれています。これは、関数が s の場合のように解析時ではなく、実行時FunctionDeclarationに評価されることを意味します。これらの実装では、定義された最初の関数を取得します。


関数はさまざまな方法で宣言できます。以下を比較してください

1-変数Multiplyに割り当てられたFunctionコンストラクターで定義された関数:

var multiply = new Function("x", "y", "return x * y;");

2- multiplyという名前の関数の関数宣言:

function multiply(x, y) {
    return x * y;
}

3- 変数multiplyに割り当てられた関数式:

var multiply = function (x, y) {
    return x * y;
};

4- 変数Multiplyに割り当てられた名前付き関数式func_name :

var multiply = function func_name(x, y) {
    return x * y;
};
于 2009-10-27T23:32:43.317 に答える
55

これは古い質問と回答ですが、今日まで多くの開発者をループに陥れているトピックについて説明しています。私がインタビューした JavaScript 開発者候補者の数は数えきれません。彼らは関数宣言と関数式の違いを教えてくれずすぐに呼び出される関数式が何であるかを知らなかったのです。

ただし、Premasagar のコード スニペットは名前識別子を付けたとしても機能しないという非常に重要なことを 1 つだけ述べておきたいと思います。

function someName() {
    alert(2 + 2);
}();

これが機能しない理由は、JavaScript エンジンがこれを関数宣言として解釈し、その後に式を含まない完全に無関係なグループ化演算子が続き、グループ化演算子には式が含まれている必要があるためです。JavaScript によると、上記のコード スニペットは次のコードと同等です。

function someName() {
    alert(2 + 2);
}

();

私が指摘したいもう 1 つのことは、関数式に指定する名前識別子は、関数定義自体の内部を除いて、コードのコンテキストではほとんど役に立たないということです。

var a = function b() {
    // do something
};
a(); // works
b(); // doesn't work

var c = function d() {
    window.setTimeout(d, 1000); // works
};

もちろん、関数定義で名前識別子を使用すると、コードのデバッグに関しては常に役立ちますが、それはまったく別のことです... :-)

于 2012-08-26T04:02:50.397 に答える
20

すばらしい回答がすでに投稿されています。ただし、関数宣言は空の完了レコードを返すことに注意してください。

14.1.20 - ランタイム セマンティクス: 評価

FunctionDeclaration : function BindingIdentifier ( FormalParameters ) { FunctionBody }

  1. NormalCompletion (空) を返します。

戻り値を取得しようとするほとんどの方法では、関数宣言が関数式に変換されるため、この事実を観察するのは簡単ではありません。しかし、evalそれを示しています:

var r = eval("function f(){}");
console.log(r); // undefined

空の完了レコードを呼び出しても意味がありません。だからこそfunction f(){}()働けない。実際、JS エンジンはそれを呼び出そうとさえしません。括弧は別のステートメントの一部と見なされます。

ただし、関数を括弧で囲むと、関数式になります。

var r = eval("(function f(){})");
console.log(r); // function f(){}

関数式は関数オブジェクトを返します。したがって、次のように呼び出すことができます(function f(){})()

于 2016-10-09T19:01:07.503 に答える
3

もう一つちょっとした発言があります。コードは小さな変更で機能します。

var x = function(){
    alert(2 + 2);
}();

より広く普及しているバージョンの代わりに、上記の構文を使用します。

var module = (function(){
    alert(2 + 2);
})();

vimのjavascriptファイルでインデントを正しく機能させることができなかったためです。vimは、開き括弧内の中括弧が気に入らないようです。

于 2011-12-02T00:53:03.683 に答える
1
(function(){
     alert(2 + 2);
 })();

括弧内に渡されたものはすべて関数式と見なされるため、上記の構文は有効です。

function(){
    alert(2 + 2);
}();

上記は有効な構文ではありません。java スクリプト構文パーサーは、何も見つからないため、関数キーワードの後に​​関数名を検索するため、エラーがスローされます。

于 2016-06-23T19:46:07.757 に答える
-1

これらの余分な括弧は、コードを含むグローバル名前空間と匿名関数の間に余分な匿名関数を作成します。また、他の関数内で宣言された Javascript 関数は、それらを含む親関数の名前空間にのみアクセスできます。グローバル スコープと実際のコード スコープの間に余分なオブジェクト (無名関数) があるため、スコープは保持されません。

于 2018-05-24T08:15:31.650 に答える