まあ、ECMA6 はまだ到着していないので、関数は JS でスコープを作成する最良の方法です。ある種の変数宣言を IIFE (即時呼び出し関数式) でラップすると、その変数はグローバルには作成されません。関数宣言についても同様です。
スクリプトからすべてのグローバル変数をクリアするという一見困難なタスクを与えられた場合、スクリプト全体を単純な でラップするだけでよく(function(){/*script here*/}());
、暗黙のグローバルにならないようにグローバルは作成されませんが、それはただの怠惰な修正です。 . このパターンはもっと強力です。
IIFE の使用については、こちら、こちら、およびこちらの両方で詳しく説明しました。
基本的な JS 関数呼び出しのライブ サイクルの並べ替えは、次のように機能します。
f();//call function
||
====> inside function, some vars are created, along with the arguments object
These reside in an internal scope object
==> function returns, scope object (all vars and args) are GC'ed
JS のすべてのオブジェクトと同様に、オブジェクトが参照されなくなるとすぐに、GC (ガベージ コレクション) のフラグが立てられます。ただし、次の点を考慮してください。
var foo = (function()
{
var localFoo = {bar:undefined};
return function(get, set)
{
if (set === undefined)
{
return localFoo[get];
}
return (localFoo[get] = set);
}
}());
IIFE が戻ると、別の関数である戻り値が foo に割り当てられます。現在localFoo
は IIFE のスコープで宣言されており、そのオブジェクトに直接アクセスする方法はありません。一見すると、GC されていると思うかもしれませんlocalFoo
。
しかし、待ってください、返されている関数 (およびに割り当てられている関数はfoo
まだそのオブジェクトを参照しているため、gcできません。つまり、スコープ オブジェクトは関数呼び出しよりも長く存続し、クロージャーが作成されます。
変数がスコープ外になるか、別の値が再割り当てされ、返された関数へのすべての参照が失われるまでlocalFoo
、オブジェクトは GC されません。foo
リンクされた回答の1つ(図付きのもの)を見てください。その回答には、使用した画像を盗んだ記事へのリンクがあります。これがまだ解決されていない場合は、これで問題が解決するはずです。
IIFE は何も返すことができませんが、関係なくそのスコープを公開します。
var foo = {};
(function(obj)
{
//obj references foo here
var localFoo = {};
obj.property = 'I am set in a different scope';
obj.getLocal = function()
{
return localFoo;
};
}(foo));
この IIFE は何も返しませんが (暗黙的undefined
に)、console.log(foo.getLocal())
空のオブジェクト リテラルをログに記録します。foo
自体も割り当てられproperty
ます。でも待ってください、私はあなたにもう1つうまくやることができます。上記のコードで foo が 1 回渡されたとします。
var bar = foo.getLocal();
bar.newProperty = 'I was added using the bar reference';
bar.getLocal = function()
{
return this;
};
console.log(foo.getLocal().newProperty === bar.newProperty);
console.log(bar ==== foo.getLocal());
console.log(bar.getLocal() === foo.getLocal().getLocal());
//and so on
これは何をログに記録しますか? 確かに、true
何度も何度もログに記録されます。JS ではオブジェクトがコピーされることはなく、その参照はコピーされますが、オブジェクトは常に同じです。あるスコープで一度変更すると、それらの変更はすべての参照間で (論理的に) 共有されます。
これは、クロージャーが最初は理解するのが難しいことを示すためのものですが、クロージャーがいかに強力であるかも示しています。さまざまな IIFE を介してオブジェクトを渡すことができ、そのたびに独自のメソッドにアクセスできる新しいメソッドを設定できます。 、他のメソッドが到達できない一意のスコープ。
注:
クローザーは、JS エンジンがガベージ コレクションを実行するのにそれほど簡単ではありませんが、最近ではそれほど大きな問題ではなくなりました。
また、時間をかけてこれらの用語をグーグルで検索してください。
IIFE も関数に名前を付けることができますが、その関数を参照できる唯一の場所はその関数のスコープ内です。
(function init (obj)
{
//obj references foo here
var localFoo = {};
obj.property = 'I am set in a different scope';
obj.getLocal = function()
{
return localFoo;
};
if (!this.wrap)
{//only assign wrap if wrap/init wasn't called from a wrapped object (IE foo)
obj.wrap = init;
}
}(foo));
var fooLocal = foo.getLocal();
//assign all but factory methods to fooLocal:
foo.wrap(fooLocal);
console.log(fooLocal.getLocal());//circular reference, though
console.log(init);//undefined, the function name is not global, because it's an expression
これは、クロージャを使用してラッパー オブジェクトを作成する方法の基本的な例にすぎません...