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);
}();
それを説明してもらえますか?
FunctionDeclaration
として解析され、関数宣言の名前識別子がmandatoryであるため、機能しません。
括弧で囲むとFunctionExpression
として評価され、関数式に名前を付けるかどうかを指定できます。
FunctionDeclaration
の文法は次のようになります。
function Identifier ( FormalParameterListopt ) { FunctionBody }
そしてFunctionExpression
s:
function Identifieropt ( FormalParameterListopt ) { FunctionBody }
ご覧のとおり、Identifier
のFunctionExpression
(識別子opt)トークンはオプションであるため、名前が定義されていない関数式を使用できます。
(function () {
alert(2 + 2);
}());
またはnamed関数式:
(function foo() {
alert(2 + 2);
}());
括弧(正式には グループ化演算子 と呼ばれる)は式のみを囲むことができ、関数式が評価されます。
2つのグラマープロダクションはあいまいな場合があり、次のようにまったく同じに見える場合があります。
function foo () {} // FunctionDeclaration
0,function foo () {} // FunctionExpression
パーサーは、それが現れる場所contextに応じて、FunctionDeclaration
かFunctionExpression
かを認識します。
上記の例では、2番目の式は式です。これは、 カンマ演算子 も式のみを処理できるためです。
一方、FunctionDeclaration
sは、実際には「Program
」コードと呼ばれるものにのみ表示されます。これは、グローバルスコープ外のコード、および他の関数のFunctionBody
内のコードを意味します。
ブロック内の関数は、予測できない動作を引き起こす可能性があるため、避ける必要があります。例:
if (true) {
function foo() {
alert('true');
}
} else {
function foo() {
alert('false!');
}
}
foo(); // true? false? why?
SyntaxError
にはステートメントのみを含めることができるため(およびECMAScript仕様では関数ステートメントを定義していないため)、上記のコードは実際にBlock
を生成する必要がありますが、ほとんどの実装は許容され、単純に2番目の関数を使用します、'false!'
に警告するもの。
Mozillaの実装(Rhino、SpiderMonkey)の動作は異なります。それらの文法にはnon-standardFunction Statementが含まれています。つまり、関数は解析時ではなくrun-timeで評価されます。 、FunctionDeclaration
sで起こるように。これらの実装では、定義された最初の関数を取得します。
関数はさまざまな方法で宣言できます 以下を比較 :
1-変数に割り当てられた Function コンストラクターで定義された関数multiply:
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;
};
これは古い質問と回答ですが、今日まで多くの開発者がループに陥っているトピックについて説明しています。関数の宣言と関数式の違いを教えてくれなかったインタビューしたJavaScript開発者候補の数を数えることはできませんandすぐに呼び出された関数式が何であるかがわからない人。
ただし、Premasagarのコードスニペットが名前識別子を与えたとしても機能しないということは、非常に重要なことです。
function someName() {
alert(2 + 2);
}();
これが機能しない理由は、JavaScriptエンジンがこれを関数宣言として解釈し、その後に式を含まない完全に無関係なグループ化演算子と、グループ化演算子mustが式を含むと解釈するためです。 JavaScriptによると、上記のコードスニペットは次のものと同等です。
function someName() {
alert(2 + 2);
}
();
私が指摘したい別のことは、関数式に提供する名前識別子は、関数定義自体の中を除いてコードのコンテキストではほとんど役に立たないということです。
var a = function b() {
// do something
};
a(); // works
b(); // doesn't work
var c = function d() {
window.setTimeout(d, 1000); // works
};
もちろん、関数定義で名前識別子を使用することは、コードのデバッグに関しては常に役立ちますが、それはまったく別のものです... :-)
素晴らしい回答がすでに投稿されています。ただし、関数宣言は空の完了レコードを返すことに注意してください。
FunctionDeclaration:
function
BindingIdentifier(
FormalParameters)
{
FunctionBody}
- Return NormalCompletion (empty)。
戻り値を取得しようとするほとんどの方法は、関数宣言を関数式に変換するため、この事実を観察するのは簡単ではありません。ただし、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(){})()
。
JavaScriptでは、これは Immediately-Invoked Function Expression(IIFE) と呼ばれます。
関数式にするには、次のことをします。
()を使用して囲みます
void演算子をその前に配置します
変数に割り当てます。
それ以外の場合は、関数定義として扱われ、次の方法で同時に呼び出す/呼び出すことはできません。
function (arg1) { console.log(arg1) }();
上記はエラーを与えます。なぜなら、関数式はすぐにしか呼び出せないからです。
これはいくつかの方法で実現できます:方法1:
(function(arg1, arg2){
//some code
})(var1, var2);
方法2:
(function(arg1, arg2){
//some code
}(var1, var2));
方法3:
void function(arg1, arg2){
//some code
}(var1, var2);
方法4:
var ll = function (arg1, arg2) {
console.log(arg1, arg2);
}(var1, var2);
上記はすべて、すぐに関数式を呼び出します。
ちょっとした発言があります。あなたのコードは小さな変更で動作します:
var x = function(){
alert(2 + 2);
}();
より広く普及したバージョンではなく、上記の構文を使用します。
var module = (function(){
alert(2 + 2);
})();
私はvimのjavascriptファイルでインデントを正しく動作させることができなかったからです。 vimは、開き括弧の中の波括弧を好まないようです。
次のようなパラメータ引数とともに使用できます。
var x = 3;
var y = 4;
(function(a,b){alert(a + b)})(x,y)
7になります
これらの追加の括弧は、グローバル名前空間とコードを含む匿名関数の間に追加の匿名関数を作成します。また、他の関数内で宣言されたJavascript関数では、それらを含む親関数の名前空間にのみアクセスできます。グローバルスコープと実際のコードスコープの間に余分なオブジェクト(匿名関数)があるため、保持されません。
おそらく、より短い答えは
function() { alert( 2 + 2 ); }
関数リテラル thatdefines(匿名)関数です。式として解釈される追加の()ペアは、トップレベルでは期待されず、リテラルのみです。
(function() { alert( 2 + 2 ); })();
expression statement thatinvokes無名関数です。
次のように使用することもできます。
! function() { console.log('yeah') }()
または
!! function() { console.log('yeah') }()
!
-否定opはfn定義をfn式に変換するため、()
を使用してすぐに呼び出すことができます。 0,fn def
またはvoid fn def
を使用するのと同じ
(function(){
alert(2 + 2);
})();
括弧内に渡されるものはすべて関数式と見なされるため、上記は有効な構文です。
function(){
alert(2 + 2);
}();
上記は有効な構文ではありません。 Javaスクリプト構文パーサーは何も見つからないため、関数キーワードの後に関数名を検索するため、エラーがスローされます。