閉包的定義有很多,但是被定義的對象確是一樣的,只不過看待問題的角度不一樣罷了。都是研究是函數(shù)跟它的作用域合在一起的一種類似于生態(tài)體系的樣子,好像母體和胎兒的意思,畢竟函數(shù)的聲明本身就是也限定了它的作用范圍,引用的變量等等都與作用域有關(guān)。
詞法作用域
函數(shù)的各個變量需要有聲明的,否則就是undefined,要找聲明,先從內(nèi)部找,再從函數(shù)當前所在的作用域(詞法作用域)找,依次往上。
函數(shù)連同它作用域鏈上找的這個變量,共同構(gòu)成閉包。
作用:
- 封裝數(shù)據(jù)
- 暫存數(shù)據(jù)
例子:
function car(){
var speed = 0
function fn(){
speed++
console.log(speed)
}
return fn
}
var speedUp = car() //邏輯就是先運行car(),就要運行fn(),就要找變量speed
speedUp() //1
speedUp() //2
如果沒有里面的fn()返回出來賦值給speedUp,speed運行了就銷毀了。
全局作用域的speedUp是一直存在的,除非關(guān)閉頁面,才會銷毀變量,局部的是運行完以后就銷毀,所以fn也是一直存在的,fn的作用域也要存在,變量speed就保存了。上面的可以這樣:
var speed = 0
function fn(){
speed++
console.log(speed)
}
fn() //1 這就是閉包的兩個要素,函數(shù)和變量。
fn() //2這里沒有return fn所以沒形成閉包,執(zhí)行了就被銷毀了。
fn() //3
案例
var fnArr =[]
for (var i =0;i<10;i++){
fnArr[i] =function(){
return i
}
}
console.log(fnArr[3]()) //10不是3,為什么?要fnArr[3](),就要執(zhí)行里面的function,就要return i,
//在哪里,在全局的for循環(huán)里,所以,這時候for循環(huán)早執(zhí)行完了。
如圖:
global全局作用域哦。
改造:
var fnArr=[]
for(var i=0;i<10;i++){
fnArr[i]=(function(j){
return function(){
return j
}
})(i)
}
console.log(fnArr[3]()) //3
立刻執(zhí)行的函數(shù),這時候的i=3造成了
(function(j){
return function(){
return j
}
})(i)的i等于3,這時候相當于:
var fn =function(j){
return function(){
return j
}
執(zhí)行fn(3),相當于找j的值,相當于在函數(shù)內(nèi)加了var j=arguments[0]
//i=3賦值給一個臨時變量,返回這個臨時變量就是這個意思了。
var fnArr=[]
for(var i=0;i<10;i++){
(function(i){
fnArr[i] = function(){
return i
}
})(i)
}
console.log(fnArr[3]())//3
這里也是將i=3的值封裝進了return i的包里了,這就是作用域與函數(shù)的奇妙結(jié)合,避免了全局下的i值為10。
var fnArr=[]
for(let i=0;i<10;i++){
fnArr[i]=function(){
return i
}
}
console.log(fnArr[3]()) //3這里是let的用法,作為拓展的,沒有閉包的相關(guān)。