JS閉包解析


三點注意事項

JS作用域傳送門

  • JS沒有塊級作用域,只有全局作用域和局部作用域(函數作用域)。
  • JS中的作用域鏈,內部的作用域可以訪問到外部作用域中的變量和方法,而外部作用域不能訪問內部作用域的變量和方法。
  • 當前作用域沒有此變量或方法,會向外部作用域尋找變量或方法。

閉包的兩種使用場景

函數作為返回值

function f() {
  var a = 100;
  return function () {
    console.log(a);
  }
}
var fn = f();
var a = 200;
fn(); // 輸出100

調用fn函數,輸出a的值,fn中並沒有定義a,所以會向上找a,在f函數的作用域中,有a,值為100。所以就會輸出100,並不會輸出200。全局作用域中的a和f函數作用域中的a並不相同。這也體現出了閉包的一個好處:不會造成全局變量污染

函數作為參數

function f() {
  var a = 100;
  return function () {
    console.log(a);
  }
}

var fn = f();

function f2(fn) {
  var a = 200;
  fn();
}

f2(fn);// 輸出100

調用f2函數,傳入fn函數,調用fn函數,輸出a為100。

關於for循環和閉包之間的關系

var arr = [];
var i;
for (i = 0; i < 3; i++) {
  arr[i] = function () {
    return i;
  }
}
console.log(arr[0]()); // 輸出2

arr[0]()的結果,按照一般的思路來講,應該是0才對,為什么是2呢?

  1. for循環3次,i的值從0變為2,arr這個數組中也添加了3個函數。
  2. 當調用arr[0]函數時,for循環已經結束了,這時候i的值已經為2了,所以arr[0]函數取到的值為2。

如何解決這個問題?

var arr = [];
var i;
for (i = 0; i < 3; i++) {
  arr[i] = (function (i) {
    return function () {
      return i;
    }
  })(i);
}

console.log(arr[0]()); // 輸出0

這里,使用自執行匿名函數構造成一個獨立作用域。每一次for循環的時候,都會執行這個匿名函數,並生成 一個匿名函數作用域。
比如第一次循環的時候,i = 0,將i作為參數傳入匿名函數中,這樣i的值就被保存在匿名函數作用域中。當調用arr[0]函數時,arr[0]就會取到匿名函數中的i的值。

閉包的實際應用

// 閉包實際應用中主要是封裝變量,收斂權限
function isFirstLoad() {
  var _list = [];
  return function (id) {
    if (_list.indexOf(id) > 0) {
      return false;
    } else {
      _list.push(id);
      return true;
    }
  }
}

var firstLoad = isFirstLoad();
firstLoad(1); // 返回true
firstLoad(1); // 返回false
firstLoad(2); // 返回true

閉包的缺點

目前主流瀏覽器采用的垃圾收集策略均為標記清除。當變量進入環境(比如,定義一個變量)時,就將這個變量標記為‘進入環境’,當變量離開環境時,就會被標記為‘離開環境’,就會被銷毀。在剛才的閉包實際應用中_list變量一直被isFirstLoad的返回函數引用着,不會隨着isFirstLoad的調用結束而銷毀。所以_list變量會一直存在內存中。因此不能濫用閉包,否則就會造成網頁性能問題,甚至內存泄漏。當我們不需要這些變量的時候,我們可以把這些變量的值賦值為null


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM