JS:關於我對閉包的理解

在《javaScript高級程序設計》裏對閉包的解釋是這樣的:閉包是指有權訪問另外一個函數做用域中的變量的函數
由此咱們可知,閉包是一個函數,在這個函數裏能夠獲取到其餘變量。javascript


函數在調用時發生了什麼

1.函數一旦被調用,它會建立一個執行環境和一個做用域鏈,你們都知道做用域鏈是用來肯定變量是否能夠被訪問到。
2.函數會先用其參數和其餘變量來初始化該函數的活動對象。
3.外部函數的活動對象則處於第二位,以此類推知道包括全局的變量對象爲止。
4.當函數須要訪問一個變量時,它會沿着做用域鏈查找。
通常來講,除了全局的變量對象,局部對象只有在函數執行期間保存,執行完函數以後就會銷燬局部的活動對象。但閉包不是css

匿名函數通常做爲閉包被返回, 一旦被返回,該匿名函數的做用域鏈還引用着的外部函數的變量對象就不會被銷燬,除非銷燬該匿名函數(設置爲null)html


閉包與變量

function createFunctions(){
    var result = new Array();
    for(var i = 0; i < 10; i++) {
        result[i] = function(){
            return i;
        }
    }
    return result;
}

在這段代碼中,原本的意思是想要返回一個數組,這個數組是一個函數數組,按理說每一個函數應該返回本身的索引。
結果如圖(運行result0
這裏寫圖片描述
運行結果爲10???簡直不敢相信。
明明就是在i = 0的時候傳入的啊。原來閉包的做用域鏈包含i,因此在被返回後沒有銷燬這個變量對象,但咱們記得在createFunction結束時i的值爲10,因此全部的匿名函數被返回時引用的都是同一個i,即10java

function createFunctions(){
    var result = new Array();
    for(var i = 0; i < 10; i++) {
        result[i] = function(num){
            return function() {
                return num;
            }
        }(i);
    }
    return result;
}
var result = createFunctions();
alert(result[0]());

結果以下
這裏寫圖片描述
主要的思想是爲了消除i的引用,又由於函數的參數都是按值傳遞的,所以能夠當即執行一個函數,令num爲i的一個副本,當即執行則是爲了讓每一個數組內的元素的num都不同。num也是被返回的匿名函數的活動對象,可是每一次result[?]都有一個num副本,所以不會發生指向同一個變量的事情es6

使用es6中的let解決問題
for(let i = 0; i < 10; i++) {
    result[i] = function(){
        return i;
    };
}

閉包與this

全局函數中的this爲window對象,匿名函數的執行環境通常是全局的,因此其this指向window對象。web

內部函數會有兩個特殊的對象:arguments和this。爲何是特殊的呢?由於函數在訪問這兩個對象時只會搜索到其變量對象爲止,而不會去往外搜索了。數組

因此,當一個閉包返回this.屬性時通常該屬性都是window對象的屬性。閉包

window.name = "amy";
    var obj = {
        name: "sam",
        age: 18,
        getName: function() {
            return function() {
                return this.name;
            }
        }
    };
alert(obj.getName()());

結果
這裏寫圖片描述svg

知道了緣由,咱們只須要將that指向obj對象,再將this改成that便可。函數

window.name = "amy";
    var obj = {
        name: "sam",
        age: 18,
        getName: function() {
            that = this;
            return function() {
                return that.name;
            }
        }
    };
alert(obj.getName()());

結果
這裏寫圖片描述


補充:閉包的具體應用(轉載)

閉包返回監聽函數

如今咱們要建立幾行字,你們都知道字體的單位爲em時字體的大小是根據其最近的父級字體大小設置的。
咱們如今要作的就是,根據點擊不一樣按鈕修改body的字體大小,從而達到修改全部字體大小的效果。
html

<body>
  <p>p tag</p>
  <h1>h1 tag</h1>
  <h2>h2 tag</h2>
  <button id="12">12px</button>  
  <button id="14">14px</button>
  <button id="16">16px</button>
</body>

css

body { font-size: 12px; }
p { font-size: 1em; }
h1 { font-size: 2em; }
h2 { font-size: 1.5em; }

js

function setFontSize(fontsize) {
  return function() {
    document.body.style.fontSize = fontsize + 'px';
  }
}
const button12 = document.getElementById('12');
const button14 = document.getElementById('14');
const button16 = document.getElementById('16');
button12.onclick = setFontSize(12);
button14.onclick = setFontSize(14);
button16.onclick = setFontSize(16);

點擊12按鈕
這裏寫圖片描述
點擊14按鈕
這裏寫圖片描述
點擊16按鈕
這裏寫圖片描述
看出來了嗎?其實setFontSize就是一個閉包,經過其傳入參數,並返回操做該參數的函數。將返回函數賦值到點擊事件上就行了。

閉包模擬私有方法

能夠經過閉包一個對象的私有方法,方法是在外面定義私有變量或私有函數,在返回一個對象,該對象包含全部的公有方法。

var Counter = function() {
  var counts = 0;
  return {
    increase: function() {
      counts++;
    },
    decrease: function() {
      counts--;
    },
    getCounts: function() {
      return counts;
    }
  }
}
var counter = Counter();
console.log('a');
console.log(counter.getCounts()); // 0
counter.increase();
counter.increase();
console.log(counter.getCounts()); // 1