JavaScriptにおけるクローズドの使用を理解する


前に閉包を勉強していた時はずっと一知半解の状態でしたが、今日は再び閉包の知識を整理します.
1.クローズドとは何ですか
簡単に言えば、クローズドとは関数に内部関数を組み込むことであり、この内部関数は外部関数のパラメータと変数を参照することができ、これらは内部関数によって参照される外部関数のパラメータと変数はゴミ回収メカニズムによって回収されない.まず簡単な例を見てもいいです.
function aaa(){
        var a = 1;
        a++;
        console.log(a);
    }
    aaa(); // 2
    aaa(); // 2
上記のプログラムは2回のa a a()関数を呼び出しましたが、2回の出力結果はすべて2で、つまり2回目の実行時に、関数はまた変数aに値を割り当てます.もしメモリに変数aの値を保持したいなら、次にaに対する参照はすべて元の基礎の上にあります.上記のコードは次のように書き換えられます.
    function aaa(){
        var a = 1;
        return function(){
            a++;
            console.log(a);
        };
    }
    var b = aaa();
    b(); // 2
    b(); // 3
上記のコードはb()関数を2回呼び出して、それぞれ2と3を出力しました.つまり、第1回の呼び出しが終わった後、ごみ回収メカニズムはパラメータを回収していません.このパラメータは呼び出しが終わった後もメモリに留まりますので、第2回の呼び出し時に直接に第1回の呼び出しに基づいて変数aを蓄積します.これはクローズド形式です.
2.閉包の役割
(1)変数をメモリに長く残しておいて、後期に引用することを希望する(この特徴は上記で紹介されているので、別に詳しく説明しない)
(2)グローバル変数の汚染を避ける
まず一つの例を見てみます.変数の呼び出しごとに1回の積算が必要であれば、1のプログラム2の機能を実現します.通常は次のような形で書きます.
    var a = 1;
    function aaa(){ a++; console.log(a); } aaa();
    aaa();
上記のコードも毎回の呼出ごとにaをアキュムレータする機能を実現することができますが、aはグローバル変数です.性能を向上させるために、グローバル変数をできるだけ避けるべきです.したがって、この機能を実現するために、クローズド・パケットを使用することが推奨されている.
(3)モジュール化コードで、関数内のメンバーを私有化する
(2)の機能をクローズドで実現し、そのモジュール化を以下のように書くことができる.
    var aaa = (function(){
        var a = 1;
        return function(){
            a++;
            console.log(a);
        };
    })();
    aaa();
    aaa();
このように、aaaは外部内名関数の返却結果であり、結果は内部の匿名関数でもあるので、直接aaを呼び出すことができます.もう一つのモジュールコードを紹介します.
var aaa = (function(){
        var b = 1;
        var c = 2;
        function bbb(){
            b++;
            console.log(b);
        }
        function ccc(){
            c+=2;
            console.log(c);
        }
        return {
            bb: bbb,
            cc: ccc
            };

        })();

        aaa.bb(); //2
        aaa.bb(); //3
        aaa.cc(); //4
        aaa.cc(); //6
パッケージをモジュール化することで、関数内のメンバーを直接取得することができず、関数でしか取得できません.このように関数内部のメンバに対して保護作用を与え,コードの多重化も増加した.
(4)サイクルの中で直接に対応する要素のインデックス値の需要説明を見つけます.ulリストを作成し、中に複数のliが存在します.あるliをクリックしたとき、対応するインデックス番号がイジェクトされます.私達のやり方では:
// html  
<ul>
    <li>111111111111</li>
    <li>222222222222</li>
    <li>333333333333</li>
    <li>444444444444</li>
</ul>

// JavaScript  
window.onload = function(){
    var aLi = document.getElementsByTagName('li');
    for (var i = 0; i < aLi.length; i++) {
        aLi[i].onclick = function(){
            alert(i);
        };
    }
};
運行後、私達はどのliをクリックしても、弾けるのは全部4です.これは、イベントが発生していない時に、forサイクルはすでに終了しました.この時のiのはもう4になりました.だから、どのliをクリックしても、出てくるのは全部4です.この時に、クローズドを利用して上記の需要を実現できます.
window.onload = function(){
    var aLi = document.getElementsByTagName('li');
    for (var i = 0; i < aLi.length; i++) {

        aLi[i].onclick = (function(i){
            return function(){
                alert(i);
            };          
        })(i);
    }
};
もちろん、クローズドしなくても、liを通して同じ効果が得られます.
window.onload = function(){
    var aLi = document.getElementsByTagName('li');
    for (var i = 0; i < aLi.length; i++) {
        aLi[i].index = i;
        aLi[i].onclick = function(){
                alert(this.index);
            };          
    }
};
3.クローズドを使う時に注意するべき問題
(1)クローズドが引き起こす最大の問題はメモリリークです.クローズドは関数の変数がずっとメモリに保存されているため、メモリの消耗が大きく、クローズドを濫用することができません.解決策は、関数を終了する前に使用しないローカル変数をすべて削除することです.
(2)クローズドによるもう一つの問題は、関数の内部変数値が関数の外部でクローズドされて変化することです.これも注意が必要です.
4.思考問題
前の筆記試験では、次の手順の結果を聞きます.
    function Foo(){
        var i = 0;
        return function(){alert(i++);};
        }
        var f1 = Foo(),f2=Foo();
        f1(); //0
        f1(); //1
        f2(); //0
f 1()を呼び出した場合、まずiの値を0とし、iの値を1として追加し、f 1()を呼び出します.このとき、第1の運転後iの値がイジェクトされるので、1をイジェクトし、iの値を1とします.その後、f 2()を呼び出します.f 1()とf 2()は異なる関数ですので、両者の運転時は影響がなく、最後のポップアップの値0です.