javascriptの中のクローズド・クローズアップの詳細解

6694 ワード

概要
クローズドclosureはjavascriptの中で非常に強力な機能です.クローズドとは、関数の関数であり、内部関数は、外部関数の作用領域範囲にアクセスすることができ、したがって、いくつかのより強力な仕事をするためにクローズドを使用することができます.
今日はクローズドバッグについて詳しく紹介します.
関数の関数
関数の関数が親関数のスコープの変数にアクセスできることを述べました.例を見ます.
function parentFunction() {
  var address = 'flydean.com'; 
  function alertAddress() { 
    alert(address); 
  }
  alertAddress();
}
parentFunction();
上記の例では、parent Functionで変数addressを定義し、parent Functionの内部でalertAddress方法を定義し、この方法の内部で外部関数で定義されたaddress変数にアクセスする.
上のコードの運行は大丈夫です.正確にデータにアクセスできます.
クローズド
関数の関数がありますが、閉じたものは何ですか?
私たちは次の例を見ます.
function parentFunction() {
  var address = 'flydean.com'; 
  function alertAddress() { 
    alert(address); 
  }
  return alertAddress;
}
var myFunc = parentFunction();
myFunc();
この例は最初の例とよく似ていますが、違いは内部関数を戻してmyFunに値を付けたことです.
次に私たちは直接myFunを呼びました.
myFunでは、parent Functionのアドレスティング変数にアクセスしましたが、すでに実行済みです.
しかし、私たちはmyFunを呼び出した時に、任意にアドレゼ変数にアクセスできます.これは閉包です
クローズドのこの特性は非常に持っています.クローズドを使ってFnction factoryを生成できます.
function makeAdder(x) {
  return function(y) {
    return x + y;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12
add 5とadd 10は全部クローズドです.彼らはmakeAddrというfunction factoryによって作成されました.異なるxパラメータを伝達することによって,異なる基底数のadd法を得た.
最終的に二つの異なるadd法を生成した.
Function factoryの概念を使用して、私達は閉じたパッケージの実用的な応用を考慮することができます.例えば、私達はページに3つのbuttonがあります.これらのbuttonをクリックすることによってフォントの機能を修正することができます.
まずfunction factoryを通じて3つの方法を生成できます.
function makeSizer(size) {
  return function() {
    document.body.style.fontSize = size + 'px';
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);
この3つの方法があります.DOM元素とcalback方法を結びつけます.
document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
閉包を使ってprvate方法を実現します.
javaと比べると、javaの中にprvateの訪問説明書があります.prvateを通じて、我々は方法を指定してクラスの内部だけに訪問することができます.
もちろん、JSにはこれはありませんが、クローズドを使っても同じ効果があります.
var counter = (function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }

  return {
    increment: function() {
      changeBy(1);
    },

    decrement: function() {
      changeBy(-1);
    },

    value: function() {
      return privateCounter;
    }
  };
})();

console.log(counter.value());  // 0.

counter.increment();
counter.increment();
console.log(counter.value());  // 2.

counter.decrement();
console.log(counter.value());  // 1.
私たちは父のfunctionでprvateCounter属性とchangeBy方法を定義しましたが、これらの方法は内部functionにしかアクセスできません.
これらの特性と方法をカプセル化して,クローズドの概念を通して外部に曝露して使用し,最終的にはプライベート変数と方法カプセル化の効果を達成した.
クローズドのScope Chin
各クローズドに対して,関数自体の作用ドメイン,親関数の作用ドメインおよび大域の作用領域を含むスコープがある.
関数の内部に新しい関数を埋め込むと、スコープchainと呼ばれるスコープチェーンが形成されます.
次の例を見てください.
// global scope
var e = 10;
function sum(a){
  return function(b){
    return function(c){
      // outer functions scope
      return function(d){
        // local scope
        return a + b + c + d + e;
      }
    }
  }
}

console.log(sum(1)(2)(3)(4)); // log 20
よくある問題を閉じます.
第一の一般的な問題は、巡回中にクローズドを使うことです.例を見ます.
function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = function() {
      showHelp(item.help);
    }
  }
}

setupHelp();
上記の例では、setupHelp関数を作成しました.setupHelpでは、Ofocusメソッドにクローズドが与えられていますので、クローズド中のitemは外部functionで定義されたitem変数にアクセスできます.
サイクル内で値を付けるので、実際には3つのクローズドパッケージを作成しましたが、この3つのクローズド共有は同じ外部関数のスコープです.
私達の本意は、違うidが異なるhelpメッセージをトリガすることです.しかし、実際に実行すれば、どのIDであれ、最終的な情報は最後となります.
Ofocusはクローズド作成後にトリガされるので、このときitemの値は実際に変化しています.ループ終了後、itemの値は最後の要素を指しています.だから、すべてが最後のデータのhelpメッセージを表示します.
この問題はどう解決しますか?
最も簡単な方法は、ES 6に導入されたlet記述子を使用して、itemをblockの作用領域範囲として定義し、その都度、ループごとに新しいitemを作成し、したがって、クローズド中のitemの値を変更しないようにする.
  for (let i = 0; i < helpText.length; i++) {
    let item = helpText[i];
    document.getElementById(item.id).onfocus = function() {
      showHelp(item.help);
    }
  }
もう一つの方法は、もう一つのクローズドを作成することです.

function makeHelpCallback(help) {
  return function() {
    showHelp(help);
  };
}

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
  }
ここでは前に述べたfunction factoryの概念を用いて、異なるクローズドのために異なる作用領域環境を作成しました.
もう一つの方法はitemを新しいfunctionのスコープに含めることです.それによって毎回作成するのは新しいitemです.これはletの原理と似ています.
  for (var i = 0; i < helpText.length; i++) {
    (function() {
       var item = helpText[i];
       document.getElementById(item.id).onfocus = function() {
         showHelp(item.help);
       }
    })(); 
  }
二つ目によくある問題はメモリリークです.
 function parentFunction(paramA)
 {
 var a = paramA;
 function childFunction()
 {
 return a + 2;
 }
 return childFunction();
 }
上記の例では、childFunctionは、parent Functionの変数aを引用しています.childFunctionがまだ使われている限り、aは解放されないので、parent Functionはゴミの回収ができなくなります.
クローズド性能の問題
オブジェクトを定義し、そのプライベート属性にはクローズドによってアクセスします.
function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
  this.getName = function() {
    return this.name;
  };

  this.getMessage = function() {
    return this.message;
  };
}
上の相手はどんな問題がありますか?
上記のオブジェクトの問題は、newごとに出てくるオブジェクトについて、getNameとgetMessage方法が複製されていることであり、コンテンツの冗長性である一方、性能の影響である.
通常、対象の方法をプロトタイプに定義します.
function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype.getName = function() {
  return this.name;
};
MyObject.prototype.getMessage = function() {
  return this.message;
};
注意してください.私たちは直接にプロジェクトの全体を書き換えないでください.これは未知のエラーを引き起こします.必要に応じて特定の方法を書き換えるだけでいいです.
締め括りをつける
クローズドはJSの中で非常に強くて有用な概念です.
本文の作者:flydeanプログラムのあれらの事
本論文のリンク:http://www.flydean.com/js-closure/
flydeanのブログ
私の公衆番号に注目してください.「プログラムに関すること」を最も分かりやすく解読し、最も深い商品、最も簡潔な教程、多くのあなたの知らない小さな技術などを発見してください.