理解閉包、立即執行函數、異步和回調

閉包

什么是閉包

一個函數使用了它外面的變量,這種用法就是閉包。閉包是一個馬后炮的總結。

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('異步任務的結果')都是回調,一個是名詞,一個是動詞。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容