javascript-----クローズド

5454 ワード

クローズド:別の関数のスコープ内の変数にアクセスできる関数を指します.最も一般的なクローズドを実現する方法は、関数の内部に別の関数を作成することである.ある関数が呼び出されると、実行環境と対応する作用ドメインチェーンが作成されます.その後、関数の活動オブジェクトを初期化するために、アーグメンントと他の命名パラメータの値を使用する.しかし、スコープ内では、外部関数の活動対象は常に第二位にあり、外部関数の外部関数の活動対象は第三位にあり、作用ドメインチェーンの終点である大域実行環境に至るまで(この部分はよく理解しておく必要がある).
(一)クローズドと変数
クローズドは、関数内の任意の変数を含む最後の値しか取得できません.クローズドされているのは変数オブジェクト全体で、特定の変数ではありません.
function createFun(){
 	var result = new Array();
 	for (var i=0; i < 10; i++){
	     result[i] = function(){ return i;};
	 }
 return result;
} 
各関数は10を返します.各関数の作用領域チェーンにはクリアー関数の活動対象が保存されているので、それらは同じ変数iを参照しています.createFun()関数が戻ると変数iの値は10であり、各関数は変数iを保存する同じ変数オブジェクトを参照しているので、各関数の内部iの値は10である.
予想通りのコードを実現する
function createFunctions(){
 	var result = new Array();
     for (var i=0; i < 10; i++){
	     result[i] = function(num){
	     	return function(){ return num; };
     	}(i);
     }
 return result;
} 
ここでは、直接にクローズドパケットの値を配列に割り当てず、匿名関数を定義し、直ちに匿名関数を実行した結果を配列に割り当てます.ここの匿名関数にはパラメータnumがあります.つまり、最後の関数が返す値です.各匿名関数を呼び出すと、変数iが入ってきます.関数パラメータは値で伝達されるので、変数iの現在値をパラメータnumにコピーします.この匿名関数の内部では、numを訪問するためのクローズドバックが作成された.このようにして、reult配列の各関数にはnum変数のコピーがありますので、それぞれの数値を返します.
(二)thisオブジェクトについて
通常の匿名関数の実行環境は大域的であり、そのthisオブジェクトは一般にwindowを指す.しかし、クローズドの作成方法によっては、この点はそれほど明らかではないかもしれません.
var name='    '
var obj={
    name:'ccc',
    getName(){
        return function(){
       	    console.log(this)  //  Window
            return this.name
        }
    }
}
console.log(obj.getName()())  //    
Obj.getName()()を呼び出すと文字列が戻ります.しかし、返ってきた文字列は「私は猫です」、つまりグローバル変数の値です.なぜ匿名関数は、スコープ(または外部スコープ)を含むthisオブジェクトを取得していないのですか?各関数は呼び出し時に自動的に2つの特殊変数を取得します.内部関数は、この2つの変数を検索すると、そのアクティブなオブジェクトだけが検索されますので、外部関数の2つの変数に直接アクセスすることはできません.外部作用領域のthisオブジェクトを一つのクローズドパケットがアクセスできる変数に保存すると、そのオブジェクトにクローズドがアクセスできます.
var name='    '
 var obj={
     name:'ccc',
     getName(){
         var me = this
         return function(){
             console.log(me)  //     obj
             console.log(this)  //  Window
             return me.name
         }
     }
 }
 console.log(obj.getName()())  //ccc
thisとargmentsも同じ問題があります.作用領域のアーグメンントオブジェクトにアクセスするには、そのオブジェクトに対する参照を別のクローズドパケットにアクセスできる変数に保存しなければなりません.
(三)メモリ漏れ
function assignHandler(){
     var element = document.getElementById("someElement");
     element.onclick = function(){alert(element.id); };
} 
以上のコードはelement要素イベントハンドラとしてのクローズドパッケージを作成しましたが、このクローズドはまた循環参照を作成しました.匿名関数は、assignHandler()の活動対象への参照を保存しているので、elementの参照数を減らすことができなくなります.匿名関数が存在する限り、elementの参照数は少なくとも1であるため、メモリの占有は永遠に回収されない.
どう解決しますか?
function assignHandler(){
     var element = document.getElementById("someElement");
     // element.id             
     var id = element.id;
     
     element.onclick = function(){alert(id); };
     element = null;
}
上のコードでは、この変数をクローズドに参照して循環参照を解除します.それでもメモリ漏れの問題は解決できません.クローズドは、関数を含むオブジェクト全体を参照し、そこにelementが含まれていることを覚えておく必要があります.閉じたパケットが直接にelementを参照しなくても、関数を含むアクティブなオブジェクトには参照が保存されます.したがって、element変数をnullに設定する必要があります.これにより、DOMオブジェクトへの参照を解除し、その参照数を順調に減少させ、メモリの占有を正常に回収することができる.
(四)ブロックレベルの作用領域を模倣する
IIIFが即座に実行する匿名関数
(function(){
 //        
})(); 
この方法は、匿名関数への参照がないので、クローズドの占有メモリ問題を低減することができる.関数が実行されさえすれば、その作用ドメインチェーンをすぐに破壊することができます.
(五)プライベート変数
厳密には、JavaScriptにはプライベートメンバーの概念がない.オブジェクトの属性はすべて公有です.しかし、プライベート変数の概念があります.関数で定義された変数は、関数の外部からこれらの変数にアクセスできないので、プライベート変数と見なされます.プライベート変数は、関数のパラメータ、局所変数、および関数内部で定義される他の関数を含みます.一つの関数の内部にクローズドパケットを作成すると、クローズドは自分のロールドメインチェーンを通してこれらの変数にもアクセスできます.この点を利用して、プライベート変数にアクセスするための共有方法を作成することができます.プライベート変数とプライベート関数にアクセスする権限を持つ共有方法を、特権方法(priveged method)と呼び、特権方法を作成する2つの方法:(1)構築関数において、特権方法を定義して実装する.
function Person(name){
     this.getName = function(){
	     return name;
     };
     this.setName = function (value) {
	     name = value;
     };
} 
しかし、構造関数で特権を定義する方法にも欠点がある.それはこの目的を達成するためにコンストラクターモードを使用しなければならないということです.構造関数モードの欠点は、各インスタンスに対して同じ新しい方法のセットを作成することであり、静的プライベート変数を使用して特権的方法を実現することで、この問題を回避することができる.
(六)静的プライベート変数
プライベートスコープにプライベート変数や関数を定義することによって、特権的な方法を作成することもできます.
(function(){
     //         
     var privateVariable = 10;
     function privateFunction(){
     	return false;
     }
     //    
     MyObject = function(){};
     //  /    
     MyObject.prototype.publicMethod = function(){
	     privateVariable++;
	     return privateFunction();
     };
})(); 
このモードはプライベートスコープを作成し,構造関数と対応する方法をカプセル化した.私有作用領域では、まず私有変数と私有関数を定義し、その後、構造関数とその共有方法を定義した.公有法は原型で定義されている.関数宣言は局所関数しか作成できないので、MyObjectを宣言する際にもvarキーワードが使用されていません.(宣言されていない変数を初期化し、常にグローバル変数を作成します.)MyObjectはグローバル変数です.プライベートスコープ外でアクセスできる.しかし、厳密なモードでは宣言されていない変数に値が割り当てられています.
              ,      。                 ,    。
           ,                 、              。
     ,                         。
     ,           ,                          。

        JavaScript         (JavaScript             ),    。
               ,            ,                。
                        ——                (  
       )    。

                 ,         。
       JavaScript                ,              ,    
                      。
                       。
              、                 ,         、  
                  。
JavaScriptの関数式とクローズドは非常に有用な特性であり、それらを利用して多くの機能を実現することができます.ただし、クローズドを作成するには、追加の機能領域を維持する必要がありますので、それらを過剰に使用するとメモリが大量に使われる可能性があります.