js 閉包理解筆記

https://developer.mozilla.org...

首先引用來自官網文檔的定義:

closure is the combination of a function and the lexical environment within which that function was declared.
閉包是一個函數和其內部公開變量的環境的集合.

簡單而言, 閉包 = 函數 + 環境閉包

第一個閉包的例子

function init() {
  var name = 'Mozilla'; // name is a local variable created by init
  function displayName() { // displayName() is the inner function, a closure
    alert(name); // use variable declared in the parent function    
  }
  displayName();    
}
init();

because inner functions have access to the variables of outer functions, displayName() can access the variable name declared in the parent function, init().
  • 其實這個栗子很簡單,displayName()就是init()內部的閉包函數,而爲啥在displayName內部能夠調用到外部定義的變量 name 呢,由於js內部函數有獲取外部函數中變量的權限

第二個栗子

var data = [
    {'key':0},
    {'key':1},
    {'key':2}
];
function showKey() {
    for(var i=0;i<data.length;i++) {
         setTimeout(function(){
             //console.log(i); //發現i輸出了3次3
            //console.log(this); // 發現 this 指向的是 Window
            data[i].key = data[i].key + 10;
            console.log(data[i].key)
         }, 1000);
    }
}
showKey();

上面這個例子能夠正確輸出 10 11 12 嗎?
答案是:並不能,而且還會報語法錯誤....函數

  • console.log(i); 發現i輸出了3次3,也就是說,在setTimeout 1000毫秒以後,執行閉包函數的時候,for循環已經執行結束了,i是固定值,並無實現咱們指望的效果。
  • console.log(this); 發現 this 指向的是 Window,也就是說,在函數內部實現的閉包函數已經被轉變成了全局函數,存儲到了內存中。

因此須要再定義一個執行函數this

var data = [
    {'key':0},
    {'key':1},
    {'key':2}
];
function showKey() {
    var f1 = function(n){
        data[i].key = data[i].key + 10;
        console.log(data[i].key)
    }
    for(var i=0;i<data.length;i++) {
         setTimeout(f1(i), 1000);
    }
}
showKey();
// 獲得預期的 10 11 12

第三個閉包的例子-柯里化(currying)

function makeAdder(x) {
  return function(y) {
    return function(z) {
        return x + y + z;
    }
  };
}

console.log(makeAdder(1)(2)(3)); // 6

// function factory it creates functions which can add a specific value to their argument
var add5 = makeAdder(5);
console.log(add5(1)(2));  // 8
console.log(add5(4)(5));  // 14
  • 這種返回function的形式就是柯里化,做用是 makeAdder 能夠做爲一個 function factory來使用。

第四個閉包的例子 - practicle closure

  • 閉包的適用場景:當你想要經過一個function來操做它關聯的數據時,閉包是頗有用的,這種使用方法是相似面向對象的。
  • 閉包一樣能夠模擬面向對象的私有變量的方法和變量的使用和獲取。
var counter = (function() {
  // private variable
  var privateCounter = 0; 

  // private function
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    changeValue: function(val) {
      changeBy(val);
    },
    value: function() {
      return privateCounter;
    }
  };   
})();

console.log(counter.value()); // logs 0 // 實現了內部屬性的獲取
counter.changeValue(2);// 實現了內部的changeBy()方法
counter.changeValue(10); 
console.log(counter.value()); // logs 12
counter.changeValue(-5);
console.log(counter.value()); // logs 7
  • counter對外暴露的方法就是 counter.changeValue, counter.value,而內部屬性 privateCounter 和 內部方法 changeBy 被隔離開了,只能經過外部方法來調用。
  • 同時咱們也能夠定義多個 counter,其內部屬性也是相互隔離的。
var makeCounter = function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }  
};

var counter1 = makeCounter();
var counter2 = makeCounter();
alert(counter1.value()); /* Alerts 0 */
counter1.increment();
counter1.increment();
alert(counter1.value()); /* Alerts 2 */
counter1.decrement();
alert(counter1.value()); /* Alerts 1 */
alert(counter2.value()); /* Alerts 0 */
相關文章
相關標籤/搜索