一直對閉包這個概念模糊不清,今天搗鼓半天,大致理通了。
閉包是什么?
有權訪問另外一個函數(shù)作用域中的變量的函數(shù)。
一般來講,作用域是由函數(shù)創(chuàng)建的(這里我們只討論函數(shù)作用域,不涉及ES6之后的塊級作用域)。每個函數(shù)的作用域都是封閉的,外部是無法訪問函數(shù)作用域中的變量。
那有沒有辦法可以從外部訪問到函數(shù)內部的變量呢
答案是有的,通過嵌套函數(shù)的方式,見下面的示例。
function change(){
var a = 1
function plus(){
a++
console.log(a)
}
return plus
}
var final = change()
final() //2
final() //3
final() //4
在該例中,我們得增加點知識點:
- 函數(shù)的作用域
- 變量的生命周期
plus函數(shù)作用域內沒有變量a,依據(jù)作用域鏈,找到父作用域中的變量a,當執(zhí)行final()時,a++,a=2。再執(zhí)行final()時,結果依然+1。
這里有點困惑,變量的生命周期起始于函數(shù)執(zhí)行時,終止于函數(shù)執(zhí)行完畢,為什么這里面函數(shù)中的變量a未被回收,數(shù)值一直遞增。
原因在于變量a被函數(shù)plus引用,final()間接引用著變量a。導致變量a未被內存回收。
閉包最大的用處在于:暴露局部變量,保持變量在內存中不被回收。
再來看些經典的閉包題來理解下思路
for(var i = 1; i< 10; i++){
function a(){
console.log(i)
}
}
a() //10
為什么得到10,很簡單。for循環(huán)中是函數(shù)表達式,當循環(huán)結束后,執(zhí)行a(),其中的i不存在按作用域鏈找到上一級的i并引用,結果為10。
怎么讓其按我們設想的0,1,2...輸出呢?
也很簡單,每次循環(huán)執(zhí)行一次函數(shù),傳參,var a = i ,運用立即執(zhí)行函數(shù),見下
for(var i = 1; i< 10; i++){
!function a(a){
console.log(a)
}(i)
}
如果糾結0~9,和輸出來為什么是10,可以參考下for循環(huán)的執(zhí)行次序