私はクロージャに関するいくつかの記事を読んでいて、これをいたるところで見ましたが、それがどのように機能するのか明確な説明はありません。
// Create a new anonymous function, to use as a wrapper
(function(){
// The variable that would, normally, be global
var msg = "Thanks for visiting!";
// Binding a new function to a global object
window.onunload = function(){
// Which uses the 'hidden' variable
alert( msg );
};
// Close off the anonymous function and execute it
})();
わかりました。新しい無名関数を作成してから実行します。それで、その後、この単純なコードはうまくいくはずです(そしてそれはそうします):
(function (msg){alert(msg)})('SO');
私の質問は、ここでどんな種類の魔法が起こるのでしょうか?私が書いたとき私はそれを考えた:
(function (msg){alert(msg)})
その後、関数 ""(msg)...のように新しい名前のない関数が作成されます。
しかし、なぜこれがうまくいかないのですか?
(function (msg){alert(msg)});
('SO');
同じ行に配置する必要があるのはなぜですか?
いくつかの投稿を教えてください、または説明をお願いします。
関数定義の後にセミコロンを入れます。
(function (msg){alert(msg)})
('SO');
上で動作するはずです。
デモページ: https://jsfiddle.net/e7ooeq6m/
この記事では、このようなパターンについて説明しました。
編集:
ECMAスクリプト仕様 を見ると、関数を定義する方法は3つあります。 (ページ98、セクション13関数定義)
var sum = new Function('a','b', 'return a + b;');
alert(sum(10, 20)); //alerts 30
function sum(a, b)
{
return a + b;
}
alert(sum(10, 10)); //Alerts 20;
var sum = function(a, b) { return a + b; }
alert(sum(5, 5)); // alerts 10
それで、あなたは尋ねるかもしれません、宣言と式の違いは何ですか?
ECMAスクリプト仕様書から:
FunctionDeclaration:機能識別子(FormalParameterListopt){FunctionBody}
FunctionExpression:機能識別子(FormalParameterListopt){機能本体}
あなたが気づくならば、 'identifier'は関数式のオプションです。そして、識別子を与えないと、無名関数が作成されます。識別子を指定できないという意味ではありません。
これは以下が有効であることを意味します。
var sum = function mySum(a, b) { return a + b; }
注意すべき重要な点は、 'mySum'はmySum関数本体の内側でのみ使用でき、外側では使用できないということです。次の例を参照してください。
var test1 = function test2() { alert(typeof test2); }
alert(typeof(test2)); //alerts 'undefined', surprise!
test1(); //alerts 'function' because test2 is a function.
これと比較して
function test1() { alert(typeof test1) };
alert(typeof test1); //alerts 'function'
test1(); //alerts 'function'
この知識を身に付けて、コードを分析してみましょう。
次のようなコードがあると
function(msg) { alert(msg); }
関数式を作成しました。そして、この関数式を括弧で囲むことで実行できます。
(function(msg) { alert(msg); })('SO'); //alerts SO.
これは自己起動関数と呼ばれます。
(function(){})
を呼び出すときにしていることは、関数オブジェクトを返すことです。あなたがそれに()
を追加するとき、それは呼び出され、ボディの中の何でも実行されます。 ;
はステートメントの終わりを表します。2回目の呼び出しが失敗するのはそのためです。
私が紛らわしいと思ったことの1つは、 "()"がグループ化演算子であるということです。
これがあなたの基本的な宣言された関数です。
例1:
var message = 'SO';
function foo(msg) {
alert(msg);
}
foo(message);
関数はオブジェクトであり、グループ化することができます。それでは関数の周りに親を投げましょう。
例2:
var message = 'SO';
function foo(msg) { //declares foo
alert(msg);
}
(foo)(message); // calls foo
宣言してすぐに同じ関数を呼び出すのではなく、呼び出すときに基本的な置換を使用して宣言できます。
例3。
var message = 'SO';
(function foo(msg) {
alert(msg);
})(message); // declares & calls foo
最後に、名前を使用して名前を使用しているのではないので、追加のfooは必要ありません。関数は匿名にすることができます。
例4。
var message = 'SO';
(function (msg) { // remove unnecessary reference to foo
alert(msg);
})(message);
あなたの質問に答えるために、例2に戻って参照してください。最初の行は、名前のない関数を宣言し、それをグループ化していますが、それを呼び出していません。 2行目は文字列をグループ化します。どちらも何もしません。 (Vincentの最初の例です。)
(function (msg){alert(msg)});
('SO'); // nothing.
(foo);
(msg); //Still nothing.
しかし
(foo)
(msg); //works
無名関数は ""という名前の関数ではありません。名前のない単なる関数です。
JavaScriptの他の値と同様に、関数には名前を作成する必要はありません。他の値と同じように実際に名前にバインドする方がはるかに便利ですが。
しかし、他の値と同様に、名前にバインドせずに使用したいことがあります。それが自発的なパターンです。
これは束縛されていない関数と数です。
function(){ alert("plop"); }
2;
したがって、他の値と同様に、それらを使用できるようにするには、変数にそれらを格納する必要があります。
var f = function(){ alert("plop"); }
var n = 2;
シンタックスシュガーを使って関数を変数にバインドすることもできます。
function f(){ alert("plop"); }
var n = 2;
しかし、それらに名前を付ける必要がなく、混乱や読みやすさが低下する場合は、ただちに使用することができます。
(function(){ alert("plop"); })(); // will display "plop"
alert(2 + 3); // will display 5
ここでは、私の関数と私の数は変数に束縛されていませんが、それでも使用することができます。
このように言って、それは自己呼び出し機能が本当の価値を持たないように見えます。ただし、JavaScriptのスコープ区切り文字は関数であり、ブロック({})ではないことに注意する必要があります。
したがって、自己起動関数は、実際にはC++、C#、またはJavaブロックと同じ意味を持ちます。つまり、内部で作成された変数がスコープの外に「リーク」することはありません。これは、グローバルスコープを汚染しないためにJavaScriptで非常に役立ちます。
それがまさにJavaScriptのしくみです。名前付き関数を宣言できます。
function foo(msg){
alert(msg);
}
そしてそれを呼び出します。
foo("Hi!");
あるいは、無名関数を宣言することもできます。
var foo = function (msg) {
alert(msg);
}
そしてそれを呼び出します。
foo("Hi!");
あるいは、関数を名前にバインドすることはできません。
(function(msg){
alert(msg);
})("Hi!");
関数は関数を返すこともできます。
function make_foo() {
return function(msg){ alert(msg) };
}
(make_foo())("Hi!");
make_foo
の本体に "var"で定義された変数は、make_foo
によって返される各関数によって閉じられることになります。これはクロージャであり、ある関数によって値に加えられた変更は他の関数からも見えるようになります。
必要に応じて、これにより情報をカプセル化できます。
function make_greeter(msg){
return function() { alert(msg) };
}
var hello = make_greeter("Hello!");
hello();
それはまさにJava以外のほぼすべてのプログラミング言語が機能することです。
あなたが示すコード
(function (msg){alert(msg)});
('SO');
2つのステートメントで構成されています。 1つ目は関数オブジェクトを生成する式です(保存されていないため、ガベージコレクションされます)。 2番目は文字列を返す式です。関数を文字列に適用するには、作成時に文字列を引数として関数に渡す必要があります(これも上記に示しています)。後で、あなたの余暇にそれを適用します。そのようです:
var f = (function (msg){alert(msg)});
f('SO');
無名関数(ラムダ関数)を変数に格納することによって、あなたは事実上それに名前を与えていることに注意してください。したがって、通常の関数も定義できます。
function f(msg) {alert(msg)};
f('SO');
これまでのコメントをまとめると:
function() {
alert("hello");
}();
変数に割り当てられていない場合は、構文エラーが発生します。コードは、閉じ括弧を構文的に正しくないものにする関数ステートメント(または定義)として解析されます。関数部分の周りに括弧を追加すると、これが関数式(または呼び出し)であることをインタプリタ(およびプログラマ)に知らせます。
(function() {
alert("hello");
})();
これは自己呼び出し関数です。つまり、呼び出しは宣言されているのと同じ行で行われるため、匿名で作成されてすぐに実行されます。この自己呼び出し関数は、引数のない関数を呼び出すためのよく知られた構文に加えて、関数の名前の前後に括弧を追加して示しています:(myFunction)();
。
よいSO議論のJavaScript関数構文 があります。
この答えは厳密には質問に関連していませんが、この種の構文機能は関数に固有のものではないことを知りたいと思うかもしれません。例えば、私たちはいつでもこのようなことをすることができます:
alert(
{foo: "I am foo", bar: "I am bar"}.foo
); // alerts "I am foo"
機能に関連します。それらはFunction.prototypeから継承したオブジェクトなので、次のようなことができます。
Function.prototype.foo = function () {
return function () {
alert("foo");
};
};
var bar = (function () {}).foo();
bar(); // alerts foo
また、関数を実行するために関数を括弧で囲む必要さえありません。とにかく、結果を変数に代入しようとしている限り。
var x = function () {} (); // this function is executed but does nothing
function () {} (); // syntax error
関数を宣言するとすぐに、関数に対してもう1つ行うことは、それらに対してnew
演算子を呼び出してオブジェクトを取得することです。以下は同等です。
var obj = new function () {
this.foo = "bar";
};
var obj = {
foo : "bar"
};
括弧なしの例:
void function (msg) { alert(msg); }
('SO');
(これがvoidの唯一の実際の使用法です、afaik)
または
var a = function (msg) { alert(msg); }
('SO');
または
!function (msg) { alert(msg); }
('SO');
同様に働きます。 void
は式を評価させ、代入と強打も評価します。最後のものは~
、+
、-
、delete
、typeof
、いくつかの単項演算子(void
もそのうちの1つ)で動作します。変数が必要なため、++
、--
のいずれかに機能しません。
改行は必要ありません。
質問者の質問に対する私の理解は、次のようなものです。
この魔法はどのように機能しますか?
(function(){}) ('input') // Used in his example
私は間違っているかもしれません。しかし、人々が慣れ親しんでいる通常のやり方は次のとおりです。
(function(){}('input') )
その理由は、JavaScriptがAKA ()
を括弧で囲み、文を含むことができず、パーサがfunctionキーワードを見つけたとき、それを関数宣言ではなく関数式として解析することを知っているからです。
出典:ブログ記事即時起動関数式(IIFE)
JavaScript関数にはもう1つプロパティがあります。同じ無名関数を再帰的に呼び出したい場合.
(function forInternalOnly(){
//you can use forInternalOnly to call this anonymous function
/// forInternalOnly can be used inside function only, like
var result = forInternalOnly();
})();
//this will not work
forInternalOnly();// no such a method exist
それは自己実行型の無名関数です。最初の括弧セットには実行される式が含まれ、2番目の括弧セットにはそれらの式が実行されます。
(function () {
return ( 10 + 20 );
})();
Peter Michauxが 括弧の重要なペア の違いについて説明します。
親の名前空間から変数を隠そうとするときに便利な構文です。関数内のすべてのコードは関数の非公開スコープに含まれています。つまり、関数の外部からはアクセスできず、本当に非公開になります。
見る:
(function (msg){alert(msg)})
('SO');
これは、多くのJavaScriptフレームワークが使用するクロージャとして無名関数を使用する一般的な方法です。
この関数はコードがコンパイルされると自動的に呼び出されます。
最初の行に;
を配置した場合、コンパイラはそれを2つの異なる行として扱いました。だからあなたは上記と同じ結果を得ることはできません。
これは次のように書くこともできます。
(function (msg){alert(msg)}('SO'));
詳細については、JavaScript /無名関数をご覧ください。
無名関数は実行時に動的に宣言される関数です。通常の関数と同じように名前が付けられていないため、これらは匿名関数と呼ばれています。
無名関数は、関数宣言の代わりに関数演算子を使って宣言されます。関数演算子を使用して、式を記述するのに有効な場所であればどこでも新しい関数を作成できます。たとえば、新しい関数を関数呼び出しのパラメータとして宣言したり、他のオブジェクトのプロパティを割り当てることができます。
これが名前付き関数の典型的な例です。
function flyToTheMoon(){alert( "ズーム!ズーム!ズーム!"); flyToTheMoon();これは無名関数として作成された同じ例です。
var flyToTheMoon = function(){alert( "ズーム!ズーム!ズーム!"); flyToTheMoon();
詳しくはこちらをご覧ください。
http://helephant.com/2008/08/23/javascript-anonymous-functions/
IIFEは、グローバル名前空間を「汚染」しないように、関数を単純に区分化してmsg
変数を隠します。実際には、単純にして、10億ドルのWebサイトを構築していない限り、以下のようにしてください。
var msg = "later dude";
window.onunload = function(msg){
alert( msg );
};
次のようにRevealing Module Patternを使用してmsg
プロパティに名前を付けることができます。
var myScript = (function() {
var pub = {};
//myscript.msg
pub.msg = "later dude";
window.onunload = function(msg) {
alert(msg);
};
//API
return pub;
}());
もう一つの視点
まず、無名関数を宣言できます。
var foo = function(msg){
alert(msg);
}
それからあなたはそれを呼び出します:
foo ('Few');
foo = function(msg){alert(msg);}なので、fooを次のように置き換えることができます。
function(msg){
alert(msg);
} ('Few');
しかし、解析時に宣言する関数の構文エラーを避けるために、無名関数全体を1組の中括弧で囲む必要があります。それなら、
(function(msg){
alert(msg);
}) ('Few');
このように、それは私にとって理解しやすいです。
これが機能しない単純な理由は、;
が無名関数の終わりを示すからではありません。これは、関数呼び出しの最後に()
がないと関数呼び出しではないためです。あれは、
function help() {return true;}
result = help();
を呼び出した場合、これは関数の呼び出しであり、trueを返します。
result = help;
を呼び出した場合、これは呼び出しではありません。結果に割り当てられるデータのようにヘルプが扱われる割り当てです。
あなたがしたことはセミコロンを追加することによって無名関数を宣言/インスタンス化することでした、
(function (msg) { /* Code here */ });
それから括弧だけを使って別のステートメントでそれを呼び出そうとしました...明らかに関数に名前がないので、これはうまくいきません:
('SO');
インタプリタは2行目の括弧を新しい命令/ステートメントと見なしているため、このようにしても機能しません。
(function (msg){/*code here*/});('SO');
それでもまだ機能しませんが、セミコロンを削除したときに機能します。なぜなら、インタプリタは空白とキャリッジを無視し、完全なコードを1つのステートメントとして認識するからです。
(function (msg){/*code here*/}) // This space is ignored by the interpreter
('SO');
結論:関数呼び出しは、括弧が含まれていなくても、別の関数によって呼び出されるなどの特定の条件下(つまり、onload = 'help')でヘルプ関数を実行しない限り、最後に()
がない関数呼び出しです。 setTimeoutとsetIntervalもこのタイプの関数呼び出しを許可していると私は信じていますし、インタプリタはとにかく背後に括弧を付け加えて「関数呼び出しは括弧なしの関数呼び出しではない」と思います。
あなたがしたとき:
(function (msg){alert(msg)});
('SO');
セミコロンのため、('SO')
の前に関数を終了しました。あなたが書いただけの場合:
(function (msg){alert(msg)})
('SO');
それが動作します。