閉包
什么是閉包
一個函數使用了它外面的變量,這種用法就是閉包。閉包是一個馬后炮的總結。
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()
那為何要這樣做呢(搞得這么麻煩):
閉包的作用
閉包常常用來「間接訪問一個變量」。換句話說,「隱藏一個變量」。
因為如果是全局變量,容易被改,如果是局部變量,別人又訪問不到。
上面這樣用閉包,就可以用dieFn()來修改lives。
閉包造成內存泄露?
內存泄露是指你用不到(訪問不到)的變量,依然占居著內存空間,不能被再次利用起來。
閉包里面的變量明明就是我們需要的變量(lives),所以不是內存泄露
為何有人說是?
因為 IE。IE 有 bug,IE 在我們使用完閉包之后,依然回收不了閉包里面引用的變量。
立即執行函數
什么是立即執行函數
聲明一個匿名函數,立即執行它,就是立即執行函數
!function (){
var lives = 30
console.log(lives)
}.call()
感嘆號可以換成 + - ~ 等符號,也可以換成括號。
那為什么要有這么個東西(好麻煩)
立即執行函數的作用
只有一個作用:創建一個獨立的作用域。
這個作用域里面的變量,外面訪問不到(即避免「變量污染」)。
這個作用不就恰恰是閉包所需要的嗎!!!
所以之前的函數可以寫成
!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 出來的總是 6,而不是 0、1、2、3、4、5
}
}
因為在點擊之前i早變成了6,每個監聽的元素都為6。
那么怎么解決這個問題呢?用立即執行函數給每個 li 創造一個獨立作用域即可
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)
}
在立即執行函數執行的時候,i 的值被賦值給 ii,此后 ii 的值一直不變。
i 的值從 0 變化到 5,對應 6 個立即執行函數,這 6 個立即執行函數里面的 ii 「分別」是 0、1、2、3、4、5。
異步+回調
什么是異步
同步:一定要等任務執行完了,得到結果,才執行下一個任務。
function taskSync = function(){
return '同步任務的返回值'
}
var result = taskSync() // 那么 result 就是同步任務的結果
otherTask() // 然后執行下一個任務
異步:不等任務執行完,直接執行下一個任務。相當于給前一個任務加個警報器,任務好了再告訴你去執行。
function taskAsync = function(){
var result = setTimeout(function(){
console.log('異步任務的結果')
}, 3000)
return result
}
var result = taskAsync() // result 不是異步任務的結果,而是一個 timer id。不懂?因為現在我是無法得到3秒后的result,只會得到他的定時器。
otherTask() // 立即執行其他任務,不等異步任務結束
什么情況下需要用到異步?
如果幾個任務互相獨立,其中一個執行時間較長,那么一般就用異步地方式做這件事。
什么是回調
callback 就是(傳給另一個函數調用的)函數。把括號里面的內容去掉,簡化成:callback 就是一種函數。
具體來講:
當一個函數 A 被作為參數傳給另一個函數時 B,那么這個函數 A 就叫做回調(名詞)。B 中調用 A 函數的過程,也叫做回調(動詞)。
那回調有什么用呢?
回調的作用
回調通常用在獲取「異步任務」的結果
之前異步的代碼也可寫成如下(為理解起見我簡化了)
function async(fn){
setTimeout(function(){
fn('異步任務的結果')
}, 3000)
return
}//函數聲明
async(function (xxx){
console.log(xxx)
}) // 函數調用。3秒后執行fn,xxx 是異步任務的結果
otherTask()
過程簡單來說就是我調了async函數,然后在async函數里它調了fn函數(此時fn相當于是我傳的參數function),調用的時候把'異步任務的結果'(此時'異步任務的結果'相當于xxx)傳了出來。
其中function (xxx){ console.log(xxx)}和fn('異步任務的結果')都是回調,一個是名詞,一個是動詞。