首頁 > web前端 > js教程 > javascript閉包(Closure)用法實例簡析_javascript技巧

javascript閉包(Closure)用法實例簡析_javascript技巧

WBOY
發布: 2016-05-16 15:29:01
原創
1146 人瀏覽過

本文實例講述了javascript閉包(Closure)用法。分享給大家參考,具體如下:

closure被翻譯成“閉包”,感覺這東西被包裝的太學術化。以下參考書本和網路資源簡單探討(理解不當之處務請留意)。

1、什麼是閉包

官方的回答:所謂“閉包”,指的是一個擁有許多變數和綁定了這些變數的環境的表達式(通常是一個函數),因而這些變數也是該表達式的一部分。

看了上面的定義,如果你不是高手,我堅信你會跟我一樣憤怒的質問:這tmd是人話嗎?
要理解閉包,還是程式碼最有說服力啊,上程式碼:

function funcTest()
{
 var tmpNum=100; //私有变量
 //在函数funcTest内定义另外的函数作为funcTest的方法函数
 function innerFuncTest(
 {
    alert(tmpNum); //引用外层函数funcTest的临时变量tmpNum
 }
 return innerFuncTest; //返回内部函数
}
//调用函数
var myFuncTest=funcTest(); 
myFuncTest();//弹出100

登入後複製

上面程式碼中,註解已經寫的清清楚楚。現在我們可以這麼理解「閉包」:在函數體內定義另外的函數作為目標物件的方法函數(範例中就是在函數funcTest內定義另外的函數innerFuncTest作為funcTest的方法函數),而這個物件的方法函數反過來引用外層函數體中的臨時變數(閉包是一種間接保持變數值的機制。範例中就是內部函數innerFuncTest引用外層函數funcTest的臨時變數tmpNum,這裡必須注意,臨時變數可以包括外部函數中聲明的所有局部變數、參數和聲明的其他內部函數)。當其中一個這樣的內部函數在包含它們的外部函數之外被呼叫時,就會形成閉包(範例中,呼叫函數的時候,myFuncTest實際呼叫的是innerFuncTest函數,也就是說funcTest的一個內部函數innerFuncTest在funcTest之外被調用,這時就創建了一個閉包)。

2、兩個利用閉包的例子

下面舉兩個例子,一個是因為閉包導致了問題,而另一個則利用閉包巧妙地通過函數的作用域綁定參數。

這兩個例子相關的HTML標記片段如下:

<a href="#" id="closureTest0">利用闭包的例子(1秒后会看到提示)</a><br />
<a href="#" id="closureTest1">由于闭包导致问题的例子1</a><br />
<a href="#" id="closureTest2">由于闭包导致问题的例子2</a><br />
<a href="#" id="closureTest3">由于闭包导致问题的例子3</a><br />

登入後複製

(1)、因閉包而導致問題

上面的HTML標記片段中有4個元素,現在要給後三個指定事件處理程序,使它們在使用者點擊時報告自己在頁面中的順序,例如:當使用者點擊第2個連結時,報告「您點擊的是第1個連結」。為此,如果編寫下列為後三個連結添加事件處理程序的函數:

function badClosureExample(){
  for (var i = 1; i <4; i++) {
    var element = document.getElementById('closureTest' + i);
    element .onclick = function(){
      alert('您单击的是第' + i + '个链接');
    }
  }
}

登入後複製

然後,在頁面載入完成後(不然可能會報錯)呼叫函數:

window.onload = function(){
  badClosureExample();
}

登入後複製

看一下運行結果,此時點擊後3個鏈接,會看到警告框中顯示什麼訊息呢? ——全都是「您點擊的是第4個連結」。是不是令你感到十分意外?為什麼?

分析:因為在badClosureExample()函數中指定給element.onclick的事件處理程序,也就是onclick那個匿名函數是在badClosureExample()函數運行完成後(使用者點擊連結時)才被呼叫的。而呼叫時,需要對變數i求值,解析程式會先在事件處理程序內部查找,但i沒有定義。然後,再到 badClosureExample()函數中查找,此時有定義,但i的值是4(只有i大於4才會停止執行for迴圈)。因此,就會取得該值-這正是閉包(匿名函數)要使用其外部函(badClosureExample)作用域中變數的結果。而且,這也是由於匿名函數本身無法傳遞參數(故而無法維護自己的作用域)所造成的。

那麼這個例子的問題要怎麼解決呢?其實方法有很多(不妨寫一下看看),我認為比較簡單直接的程式碼:

function popNum(oNum){
  return function(){
          alert('您单击的是第'+oNum+'个链接');
  }
}
function badClosureExample(){
  for (var i = 1; i <4; i++) {
    var element = document.getElementById('closureTest' + i);
    element .onclick =new popNum(i);
    }
}

登入後複製

(2)、巧妙利用閉包綁定參數

還是上面的HTML片段,我們要在使用者點擊第一個連結時延時彈出一個警告框,怎麼實現?答案是使用setTimeout()函數,這個函數會在指定的毫秒數之後呼叫一個函數,如:

複製程式碼 程式碼如下:
setTimeout(someFunc,1000);

但問題是,無法給其中的someFunc函數傳遞參數。而使用閉包則可以輕鬆解決這個問題:

function goodClosureExample(oMsg){
  return function(){
    alert(oMsg);
  };
}

登入後複製

函數goodClosureExample用來傳回一個匿名函數(閉包)。而我們可以透過為它傳遞參數來使傳回的匿名函數綁定該參數,如:

複製程式碼 程式碼如下:
var good = goodClosureExample('這個參數是透過閉包綁定的');

而此时,就可以将绑定了参数的good函数传递给setTimeout()实现延时警告了:
复制代码 代码如下:
setTimeout(good,1000) //此时good中已经绑定了参数

最后,测试通过的完整代码:

window.onload = function(){
  var element = document.getElementById('closureTest0');
  if (element) {
    var good = goodClosureExample('这个参数是由闭包绑定的');
    element.onclick = function(){
      setTimeout(good, 1000); //延迟1秒弹出提示
    }
  }
}

登入後複製

3、javascript的垃圾回收原理

(1)、在javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收;

(2)、如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。

在js中使用闭包,往往会给javascript的垃圾回收器制造难题。尤其是遇到对象间复杂的循环引用时,垃圾回收的判断逻辑非常复杂,搞不好就有内存泄漏的危险,所以,慎用闭包。ms貌似已经不建议使用闭包了。

希望本文所述对大家JavaScript程序设计有所帮助。

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板