JavaScriptの深度関数(二)

17033 ワード

前のページでは、関数の実行過程と原理について説明しました.本稿では関数の他の2つの特殊表現を紹介します.
 
一つの閉鎖
1,  クローズドの形成
前に述べましたが、関数の実行が終わったら、すぐに自分のAOオブジェクトを廃棄します.しかし、次のような場合:サブ関数の定義があり、サブ関数を返します.これは本当に自分のAOオブジェクトを完全に破壊しましたか?
1 function fn(){
2     var a = 1;
3     function son(){
4          console.log(a);
5     }
6     return son;
7 }
8 var test = fn();
9 test();//error ? 1
これは何をプリントしますか?表面的には、ソニー内には変数がないという声明がありますが、consol.log()アクセスaは間違っていると思います.
しかし、実際には、test()は1をプリントします.これはなぜですか?前の記事関数のスコープの役割を思い出します.
fn実行時:fn.[[scope]-{0:AO(fn)、1:GO}実行中のsonが宣言されました.son.[scope]  --- {0:AO(fn)、1:GO}
return sonはこの属性を保持します.fnはすでに実行済みです.
fn.[[scope]-{0:GO}(AO(fn)は破壊されますか?);
test()実行時までtest.[[csope]  --- {0:AO(son)、1:AO(fn)、2:GO}(testはsonのもう一つの引用で、実際には彼らは同じ関数です.)
この時testは変数aにアクセスしたいです.彼はまず自分のAO内で検索します.いいえ、fnのAOの中に探しに行きます.ちょうどあるので、最終的に印刷したのは1です.
       ここではfnで破壊されたように見えるAO(fn)は、実際にsonに引用されていますので、本当に完全に破壊されたわけではありません.fnにとっては、このオブジェクトに対する参照はすでに廃棄されています.廃棄されたように見えます.これはまだsonに保留されているAOの対象です.つまり、クローズドと言います.クローズドは他の関数内部の変数の読み取りを助けることができ、2つの関数をつなぐブリッジとして機能します.
         まとめてみます.JavaScripeにクローズドを形成するには3つの要素が必要です.
1,  親関数にサブ関数が定義されています.
2,  サブ関数内で親関数の変数にアクセスしました.
3,  サブ関数が返されます.
 
2,クローズドの応用
a)変数の私有化は、グローバル変数の効果を実現することができます.
 1 function add(){
 2     var count = 0;
 3     return function (){
 4         count ++;
 5         //some code
 6         console.log(count);
 7     }
 8 }
 9 var myAdd = add();
10 myAdd();//1
11 myAdd();//2
12 myAdd();//3
b)キャッシュとして使用(類似)する.
 1 function person(){
 2     var money = 0;
 3     var obj = {
 4         pay:function (){
 5             if(money > 0){
 6                 console.log("I spent one yuan.");
 7                 money --;
 8             }else{
 9                 console.log("I run out of my money.");
10             }
11         },
12         make:function (){
13             console.log("I made one yuan.");
14             money ++;
15         }
16     };
17     return obj;
18 }
19 var person1 = person();
20 person1.pay();//"I run out of my money."
21 person1.make();//"I made one yuan."
22 person1.pay();//"I spent one yuan."
c)モジュール開発により、グローバル変数汚染を防止する.
 1 var a = "Global";
 2 function p0(){
 3     console.log(a);
 4 }
 5 function p1(){
 6     var a = "p1";
 7     return function(){
 8         console.log(a);
 9     };
10 }
11 function p2(){
12     var a = "p2";
13     return function(){
14         console.log(a);
15     };
16 }
17 var myP1 = p1();
18 var myP2 = p2();
19 
20 p0();//"Global"
21 myP1();//"p1"
22 myP2();//"p2"
大型プロジェクトは一般的に複数の人が共同で開発しています.一人一人が異なるモジュールを担当しています.避けられないのは、同じ変数名を使っているかもしれません.これはグローバル変数汚染を引き起こします.クローズドを使えば、この問題を解決できます.次のセクションの即時実行関数を理解し、このコードを最適化することもできます.
 
二番目     直ちに関数を実行します
         すぐに関数を実行することを認識する前に、まず実行符()の二つの特徴を理解しましょう.
         1)式だけが()実行されます.
1 function test(){
2     console.log(1);
3 }();//error       
4 var test = function (){
5     console.log(1);
6 }();//1        
         2)()で実行できる表現は、関数名をシステムに無視されます.
1 var test = function (){
2     console.log(1);
3 }();//1
4 console.log(test);//undefined
5 //         :       test,          ,     ()        ,      1。
   ,  test                 ,      undefined,         ,        test      。
これは実行記号の第二の特徴をよく確認しました.
1,すぐに関数の形式を実行します.
「()」括弧は実際に演算優先度を表す数学演算子であることを知っています.もちろん、関数宣言を括弧で包んで、表現にします.これにより、()実行符を使ってすぐに実行し、関数実行の結果を得ることができます.
1 (function test(){
2     console.log(1);
3 }());//1
4 //  ()         ,         
5 (function (){
6     console.log(1);
7 });//1
以上が即実行関数の最終形式です.また、()アクチュエータを関数声明の括弧の外に置いてもいいです.
1 (function (){
2     console.log(1);
3 })();
2,直ちに関数の特徴を実行します.
実行符の特徴が分かりました.
1)直ちに実行関数が宣言されたら、すぐに関数内のコードを実行します.
2)実行が終わったらすぐに廃棄し、メモリに保存されない.
3)一度しか実行できないので、コードブロック多重の機能を果たすことができません.
上記の特徴に加えて、即時実行関数と普通関数の機能は全く同じです.
3,直ちに関数を実行して閉じている経典の応用を結び付けます.
 1 function fn(){
 2     var arr = [];
 3     for(var i = 0; i < 10; i++){
 4         arr[i] = function () {
 5             console.log(i);
 6         };
 7     }
 8     return arr;
 9 }
10 var myArr = fn();
11 myArr.map(function (item){
12     item();
13 });//10 10 10 10 10 10 10 10 10 10 
私達は順番に1-9を出力したいです.なぜ10個の10個が走ってきましたか?
よく考えてみると、これはすべてのサブ関数とfnが同じクローズドに形成されているので、最後に10をプリントしました.どうやって私たちが欲しい機能を実現できますか?
 1 function fn(){
 2     var arr = [];
 3     for(var i = 0; i < 10; i++){
 4        (function (j){
 5             arr[j] = function () {
 6                 console.log(j);
 7             }
 8        }(i));
 9     }
10     return arr;
11 }
12 var myArr = fn();
13 myArr.map(function (item){
14     item();
15 });//0 1 2 3 4 5 6 7 8 9    
即座実行関数を利用して定義された即実行の特性を利用して、毎回の循環において、iの値を即座実行関数として実行している実パラメータを伝達し、各即実行関数とサブ関数が別々のクローズドを形成すると、最終的なサブ関数は実行時にアクセスしたのは、それぞれのクローズド(即座実行関数のAO)のiの値です.これで私たちが欲しい結果が得られます.
 
クローズドは多くの場所で大きな役割を果たしますが、クローズドは自身の欠陥もあります.クローズドはメモリ空間を占有しています.深刻な場合はメモリが漏れ、システムが崩壊することもあります.だから、私たちはできるだけクローズドを使わないようにします.もし他の方法がないなら、使用後に手動でメモリの占有を解除します.例えば、参照を関数の変数の割当値に戻します.nullです.
 
学習時間は一日をおすすめします.