JavaScript之閉包(重新認識)
最近又重新學習了閉包,發現之前沒有深刻理解作用域鏈,學習作用域鏈後對閉包才可以做到真正的理解。
閉包是指有權另一個函數作用域中變量的函數。要理解閉包首先理解作用域鏈。
執行環境定義了變量或函數有權訪問的其他數據,決定了它們各自的行為。每個執行環境都有與之關聯的變量對象,保存了環境中定義的所有變量和函數。只有解析器在處理數據是我才可以訪問這個變量。當代碼在一個環境中執行時,會創建變量對象的一個作用域鏈,它保證了執行環境對有權訪問的變量和函數的有序訪問。
當某個函數被調用時,會創建一個執行環境及相應的作用域鏈。作用域的最前端,始終都是當前執行代碼所在環境的變量對象,下一個變量對象來自外部環境,以此類推,直到全局作用域中的變量對象作為作用域鏈的終點。作用域鏈中一定不會包含其內部子函數的變量對象,但子函數的作用域鏈包含函數的局部變量,因此這決定創建閉包的方法
閉包與變量
閉包只能取得函數中任何變量的最後一個值,如下創建一個數組函數
function creatFunction(){ var result = new Array(); for (var i=0; i<10; i++){ result[i] = function(){ return i; }; } return result; }
表面上每個函數都會返回自己的索引,實際上都返回10。這是因為每個函數都引用作用域鏈中同一個變量i,當creatFunction()函數返回後,i的值是10。解決方法
(1) 創建匿名函數封裝
function creatFunction(){ var result = new Array(); for (var i=0; i<10; i++){ result[i] = function(num){ function(){ return num; }; }(i) } return result; }
(2)使用ES6的let
ES6 新增了let命令,用來聲明變量。它的用法類似於var,但是所聲明的變量,只在let命令所在的代碼塊內有效。
function creatFunction(){ var result = new Array(); for (let i=0; i<10; i++){ result[i] = function(){ return i; }; } return result; }
閉包特性
(1)封閉性:外界無法訪問閉包內部的數據,如果在閉包內聲明變量,外界是無法訪問的,除非閉包主動向外界提供訪問接口;
(2)持久性:一般的函數,調用完畢之後,局部活動對象就會被銷毀,內存中僅保存全局作用域。而對於閉包來說,在外部函數被調用之後,閉包結構依然保存在。
閉包問題
(1) 占用內存。由於閉包攜帶包含它函數的作用域,因此會比其他函數占用更多內存。
(2) 引起內存泄漏。 閉包中引用HTML元素將無法被銷毀。解決方法是將需要使用的DOM元素副本的屬性保存在變量中,結束會銷毀DOM元素。
JavaScript之閉包(重新認識)