この記事の例では、JavaScript クロージャー (Closure) の使用法について説明します。参考のために皆さんと共有してください。詳細は次のとおりです:
closureは「閉鎖」と訳されますが、これはあまりにもアカデミックにパッケージ化されているように感じます。書籍やネット上の資料を参考にしながら簡単に説明してみます(不適切な理解に注意してください)。
1. クロージャとは
公式の回答: いわゆる「クロージャ」は、多くの変数とこれらの変数にバインドされた環境を含む式 (通常は関数) を指します。したがって、これらの変数も式の一部です。
上記の定義を読んだ後、もしあなたが専門家でないなら、あなたもきっと私と同じように怒ってこう尋ねると思います。「これはTMDは人間の言語なのか?」
クロージャを理解するには、次のコードが最も説得力があります:
function funcTest() { var tmpNum=100; //私有变量 //在函数funcTest内定义另外的函数作为funcTest的方法函数 function innerFuncTest( { alert(tmpNum); //引用外层函数funcTest的临时变量tmpNum } return innerFuncTest; //返回内部函数 } //调用函数 var myFuncTest=funcTest(); myFuncTest();//弹出100
上記のコードでは、コメントがわかりやすく書かれています。これで、「クロージャ」を次のように理解できます。関数本体に別の関数を対象オブジェクトのメソッド関数として定義し(この例では、関数 funcTest 内の funcTest のメソッド関数として別の関数 innerFuncTest を定義します)、メソッド関数このオブジェクトの一時変数を外部関数本体で参照するようになります(クロージャは変数の値を間接的に保持する仕組みです。この例では、内部関数 innerFuncTest は外部関数 funcTest の一時変数 tmpNum を参照します)。ここで、一時変数はすべての宣言されたローカル変数、パラメーター、およびその他の宣言された内部関数に含めることができることに注意してください。これらの内部関数の 1 つが、それらを含む外部関数の外側で呼び出される場合、クロージャーが形成されます (この例では、関数を呼び出すときに、myFuncTest は実際に innerFuncTest 関数を呼び出します。これは、funcTest の内部関数、innerFuncTest が外部で呼び出されるということを意味します) funcTest、クロージャが作成されます)。
2. クロージャを使用する 2 つの例
ここでは 2 つの例を示します。1 つはクロージャが問題を引き起こすため、もう 1 つはクロージャを使用して関数のスコープを通じてパラメータを巧妙にバインドするためです。
これら 2 つの例に関連する HTML マークアップ フラグメントは次のとおりです:
<a href="#" id="closureTest0">利用闭包的例子(1秒后会看到提示)</a><br /> <a href="#" id="closureTest1">由于闭包导致问题的例子1</a><br /> <a href="#" id="closureTest2">由于闭包导致问题的例子2</a><br /> <a href="#" id="closureTest3">由于闭包导致问题的例子3</a><br />
(1) クロージャによる問題点
上記の HTML タグ フラグメントには 4 つの 要素があります。ここで、ユーザーがクリックしたときにページ内の順序を報告するようにイベント ハンドラーを割り当てる必要があります。 2 番目のリンクにリンクすると、「1 番目のリンクをクリックしました」と報告されます。これを行うには、最後の 3 つのリンクにイベント ハンドラーを追加する次の関数を作成します。
function badClosureExample(){ for (var i = 1; i <4; i++) { var element = document.getElementById('closureTest' + i); element .onclick = function(){ alert('您单击的是第' + i + '个链接'); } } }
次に、ページがロードされた後にこの関数を呼び出します (そうでないとエラーが報告される可能性があります):
window.onload = function(){ badClosureExample(); }
最後の 3 つのリンクをクリックすると、実行結果を確認します。警告ボックスにはどのような情報が表示されますか? ——すべて「4番目のリンクをクリックした」だけです。驚きますか?なぜ?
分析: badClosureExample() 関数の element.onclick に割り当てられたイベント ハンドラー、つまり、onclick 匿名関数は、 badClosureExample() 関数の完了後 (ユーザーがリンクをクリックしたとき) にのみ呼び出されるためです。呼び出し時には、変数 i を評価する必要があります。パーサーは最初にイベント ハンドラー内を検索しますが、i は定義されていません。次に、 badClosureExample() 関数を検索します。この時点では定義されていますが、i の値は 4 です (i が 4 より大きい場合にのみ for ループの実行が停止します)。したがって、その値が取得されます。これは、クロージャ (匿名関数) がその外部関数 (badClosureExample) のスコープ内で変数を使用する場合に実行される動作とまったく同じです。さらに、これは、匿名関数自体がパラメーターを渡すことができない (したがって、独自のスコープを維持できない) という事実によっても引き起こされます。
それでは、この例の問題をどのように解決すればよいでしょうか?実際には、多くのメソッドがあります (自分で書いて見てみるのも良いでしょう)。コードは比較的単純で直接的だと思います。
function popNum(oNum){ return function(){ alert('您单击的是第'+oNum+'个链接'); } } function badClosureExample(){ for (var i = 1; i <4; i++) { var element = document.getElementById('closureTest' + i); element .onclick =new popNum(i); } }
(2)、パラメータをバインドするためのクロージャの賢明な使用法
上記の HTML フラグメントでは、ユーザーが最初のリンクをクリックしたときに警告ボックスが表示されるのを遅らせたいと考えています。これを実現するにはどうすればよいでしょうか。答えは、setTimeout() 関数を使用することです。この関数は、次のような、指定されたミリ秒数の後に関数を呼び出します。
function goodClosureExample(oMsg){ return function(){ alert(oMsg); }; }
window.onload = function(){ var element = document.getElementById('closureTest0'); if (element) { var good = goodClosureExample('这个参数是由闭包绑定的'); element.onclick = function(){ setTimeout(good, 1000); //延迟1秒弹出提示 } } }
3、javascript的垃圾回收原理
(1)、在javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收;
(2)、如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。
在js中使用闭包,往往会给javascript的垃圾回收器制造难题。尤其是遇到对象间复杂的循环引用时,垃圾回收的判断逻辑非常复杂,搞不好就有内存泄漏的危险,所以,慎用闭包。ms貌似已经不建议使用闭包了。
希望本文所述对大家JavaScript程序设计有所帮助。