閉包
什么是閉包
一個(gè)函數(shù)使用了它外面的變量汛骂,這種用法就是閉包罕模。閉包是一個(gè)馬后炮的總結(jié)。
function xxx(){
var lives = 30
var bug = 'salkdjaslkdjaslkjd...100MB' // IE bug
function die(){
lives -= 1
return lives
}
return die
}
var dieFn = xxx()
// here
var currentLives = dieFn()
那為何要這樣做呢(搞得這么麻煩):
閉包的作用
閉包常常用來(lái)「間接訪問一個(gè)變量」帘瞭。換句話說淑掌,「隱藏一個(gè)變量」。
因?yàn)槿绻侨肿兞康睿菀妆桓呐淄螅绻蔷植孔兞空┖罚瑒e人又訪問不到。
上面這樣用閉包兽埃,就可以用dieFn()來(lái)修改lives侥钳。
閉包造成內(nèi)存泄露?
內(nèi)存泄露是指你用不到(訪問不到)的變量柄错,依然占居著內(nèi)存空間舷夺,不能被再次利用起來(lái)。
閉包里面的變量明明就是我們需要的變量(lives)售貌,所以不是內(nèi)存泄露
為何有人說是给猾?
因?yàn)?IE。IE 有 bug颂跨,IE 在我們使用完閉包之后敢伸,依然回收不了閉包里面引用的變量。
立即執(zhí)行函數(shù)
什么是立即執(zhí)行函數(shù)
聲明一個(gè)匿名函數(shù)恒削,立即執(zhí)行它池颈,就是立即執(zhí)行函數(shù)
!function (){
var lives = 30
console.log(lives)
}.call()
感嘆號(hào)可以換成 + - ~ 等符號(hào),也可以換成括號(hào)钓丰。
那為什么要有這么個(gè)東西(好麻煩)
立即執(zhí)行函數(shù)的作用
只有一個(gè)作用:創(chuàng)建一個(gè)獨(dú)立的作用域躯砰。
這個(gè)作用域里面的變量,外面訪問不到(即避免「變量污染」)携丁。
這個(gè)作用不就恰恰是閉包所需要的嗎W列!梦鉴!
所以之前的函數(shù)可以寫成
!function xxx(){
var lives = 30
var bug = 'salkdjaslkdjaslkjd...100MB' // IE bug
function die(){
lives -= 1
return lives
}
return die
}.call()
舉例:
var liList = ul.getElementsByTagName('li')
for(var i=0; i<6; i++){
liList[i].onclick = function(){
alert(i) // 為什么 alert 出來(lái)的總是 6李茫,而不是 0、1肥橙、2魄宏、3、4快骗、5
}
}
因?yàn)樵邳c(diǎn)擊之前i早變成了6娜庇,每個(gè)監(jiān)聽的元素都為6。
那么怎么解決這個(gè)問題呢方篮?用立即執(zhí)行函數(shù)給每個(gè) li 創(chuàng)造一個(gè)獨(dú)立作用域即可
var liList = ul.getElementsByTagName('li')
for(var i=0; i<6; i++){
!function(ii){
liList[ii].onclick = function(){
alert(ii) // 0、1励负、2藕溅、3、4继榆、5
}
}(i)
}
在立即執(zhí)行函數(shù)執(zhí)行的時(shí)候巾表,i 的值被賦值給 ii汁掠,此后 ii 的值一直不變。
i 的值從 0 變化到 5集币,對(duì)應(yīng) 6 個(gè)立即執(zhí)行函數(shù)考阱,這 6 個(gè)立即執(zhí)行函數(shù)里面的 ii 「分別」是 0、1鞠苟、2乞榨、3、4当娱、5吃既。
異步+回調(diào)
什么是異步
同步:一定要等任務(wù)執(zhí)行完了,得到結(jié)果跨细,才執(zhí)行下一個(gè)任務(wù)鹦倚。
function taskSync = function(){
return '同步任務(wù)的返回值'
}
var result = taskSync() // 那么 result 就是同步任務(wù)的結(jié)果
otherTask() // 然后執(zhí)行下一個(gè)任務(wù)
異步:不等任務(wù)執(zhí)行完,直接執(zhí)行下一個(gè)任務(wù)冀惭。相當(dāng)于給前一個(gè)任務(wù)加個(gè)警報(bào)器震叙,任務(wù)好了再告訴你去執(zhí)行。
function taskAsync = function(){
var result = setTimeout(function(){
console.log('異步任務(wù)的結(jié)果')
}, 3000)
return result
}
var result = taskAsync() // result 不是異步任務(wù)的結(jié)果散休,而是一個(gè) timer id捐友。不懂?因?yàn)楝F(xiàn)在我是無(wú)法得到3秒后的result溃槐,只會(huì)得到他的定時(shí)器匣砖。
otherTask() // 立即執(zhí)行其他任務(wù),不等異步任務(wù)結(jié)束
什么情況下需要用到異步昏滴?
如果幾個(gè)任務(wù)互相獨(dú)立猴鲫,其中一個(gè)執(zhí)行時(shí)間較長(zhǎng),那么一般就用異步地方式做這件事谣殊。
什么是回調(diào)
callback 就是(傳給另一個(gè)函數(shù)調(diào)用的)函數(shù)拂共。把括號(hào)里面的內(nèi)容去掉,簡(jiǎn)化成:callback 就是一種函數(shù)姻几。
具體來(lái)講:
當(dāng)一個(gè)函數(shù) A 被作為參數(shù)傳給另一個(gè)函數(shù)時(shí) B宜狐,那么這個(gè)函數(shù) A 就叫做回調(diào)(名詞)。B 中調(diào)用 A 函數(shù)的過程蛇捌,也叫做回調(diào)(動(dòng)詞)抚恒。
那回調(diào)有什么用呢?
回調(diào)的作用
回調(diào)通常用在獲取「異步任務(wù)」的結(jié)果
之前異步的代碼也可寫成如下(為理解起見我簡(jiǎn)化了)
function async(fn){
setTimeout(function(){
fn('異步任務(wù)的結(jié)果')
}, 3000)
return
}//函數(shù)聲明
async(function (xxx){
console.log(xxx)
}) // 函數(shù)調(diào)用络拌。3秒后執(zhí)行fn,xxx 是異步任務(wù)的結(jié)果
otherTask()
過程簡(jiǎn)單來(lái)說就是我調(diào)了async函數(shù)俭驮,然后在async函數(shù)里它調(diào)了fn函數(shù)(此時(shí)fn相當(dāng)于是我傳的參數(shù)function),調(diào)用的時(shí)候把'異步任務(wù)的結(jié)果'(此時(shí)'異步任務(wù)的結(jié)果'相當(dāng)于xxx)傳了出來(lái)春贸。
其中function (xxx){ console.log(xxx)}和fn('異步任務(wù)的結(jié)果')都是回調(diào)混萝,一個(gè)是名詞遗遵,一個(gè)是動(dòng)詞。