javascript_function(閉包Closure)

javascript閉包(Closure)

所謂「閉包」,指的是一個擁有許多變量和綁定了這些變量的環境的表達式(一般是一個函數),於是這些變量也是該表達式的一部分。javascript

上面是官方的解釋,但這解釋只會讓人頭暈。要理解閉包,首先理解兩點:變量的做用域以及做用域鏈,這兩個在前面都已經介紹過了,而且舉了簡單了列子,來回顧一下:java

var color = "blue";  
function changeColor(){     
    var anotherColor = "red";  
    function swapColors(){         
        var tempColor = anotherColor;         
        anotherColor = color;         
        color = tempColor; 
        //在 swapColors函數裏面能夠訪問tempColor,anotherColor和color          
    } 
    //在這裏能夠訪問anotherColor和color,但不能訪問tempColor 
    swapColors(); 
}  

上面的代碼,咱們以前用來講明做用域鏈,也就是變量從裏往外找變量,這一簡單的概念。可是,如今反過來,若是,咱們須要在一個外部執行環境裏面,訪問內部執行環境的變量怎麼辦呢?閉包

function f1(){
    var n=999;
    function f2(){
        alert(n); // 999
    }
    //f2();
}

上面的代碼很簡單,咱們在f1裏面去調用f2()固然能夠彈出n的值999,可是若是咱們在外部全局執行環境裏面還要得到n的值,這又該怎麼辦?這就是剛剛的提問,外部的執行環境要求訪問內部執行環境的值。 從外往裏訪問,這裏就能夠經過閉包來實現這個效果,把上面的代碼稍做修改:函數

function f1(){
    var n=999; //私有變量

    //在函數f1內定義另外的函數做爲f1的方法函數
    function f2(){
        alert(n); //引用外層函數f1的臨時變量n
    }
    return f2; //返回內部函數
}
//調用函數
var result=f1();
result(); // 999

當其中一個這樣的內部函數在包含它們的外部函數以外被調用時,就會造成閉包(示例中,調用函數的時候,result實際調用的是f2函數,也就是說f1的一個內部函數f2在f1以外被調用,這時就建立了一個閉包)。spa

在看一個簡單例子:code

function foo() {
    var a = 1;
    function geta() {
        a++;
        return a;
    }
    return geta
}

myfunc = foo()
myfunc() // return 2
myfunc() //return 3

上面只是一個說明閉包的例子,在實際應用中並不常見。下面咱們來看看實際應用中會遇到閉包的狀況blog

狀況一 界面上有一組a標籤,分別點擊,但願點擊不一樣的a標籤彈出不一樣的結果,代碼以下:ip

<ul>
    <li><a href="#">第0個連接</a></li>
    <li><a href="#">第1個連接</a></li>
    <li><a href="#">第2個連接</a></li>
    <li><a href="#">第3個連接</a></li>
</ul>    

var as = document.getElementsByTagName("a");
for(var i=0;i<as.length;i++){
    as[i].onclick = function(){
        alert("你如今單擊的是第" + i + "個連接");
    }
}

上面這段代碼的估計你們都遇到過相似的,想要的效果和實際出現的效果不一致,原本想點擊每個,彈出不一樣的i的值,可是沒想到最後彈出的i的值都是4。 這正是因爲變量的做用域鏈影響形成的,由於在用戶單擊a標籤的時候纔會調用onclick指向的匿名函數。而調用時,須要獲得變量i的值,js解析程序首 先會在匿名函數內部查找i,可是沒有定義。因而往外找,在外部執行環境中找到變量i,可是這個i已經被循環到4了,由於for循環在頁面初始化的時候已經 被執行了。因此匿名函數裏面的i實際上取得的是外部做用域鏈中i的值。想要改正這個錯誤,其實就是把i的值傳入到匿名函數值就好了。可是匿名函數又不能傳 值。這個時候,咱們就能夠用閉包。 先建立這樣一個函數:內存

function closureTest(num){
    return function(){
        alert("你如今單擊的是第" + num + "個連接")
    }
}

這個函數直接返回了一個匿名函數,以前講過閉包,因此,當返回的這個函數在外部被接收的時候,外層函數closureTest裏的變量num,就會被保存起來,因此,將以前循環的代碼修改一下:作用域

for(var i=0;i<as.length;i++){
    as[i].onclick = closureTest(i);
}

循環裏面onclick調用的函數closureTest,並傳入了參數i,而closureTest返回了匿名函數,因此根據閉包的原理,傳入的參數i,就被保存在了內存當中,外部訪問的就是每次不同的值了。固然,你也能夠直接寫成下面這個樣子:

for(var i=0;i<as.length;i++){
    as[i].onclick = (function(i){
        return function(){
            alert("你如今單擊的是第" + i + "個連接")
        }
    })(i);
}

狀況二 利用閉包巧妙地傳遞參數,好比,有這樣的場景,點擊標籤,而後延遲彈出一句話,而而這句話,是用參數傳遞過去的。先看下面的代碼:

<a href="#">點擊我</a>

var link = document.getElementById("myLink");
link.onclick = function(){
    setTimeout(function(){
        alert("你點擊了點擊個人超連接!");
    },1000);
}

這段代碼,就是點擊1秒後,彈出"你點擊了點擊個人超連接!",固然這裏是直接把話寫在了function裏面,若是這段話是從外面傳過來的呢?好比有個方法

function someFunction(words){
    alert(words);
}
var link = document.getElementById("myLink");
link.onclick = function(){
    setTimeout(someFunction,1000);
};

這裏的話,問題就來了。怎麼往someFunction傳遞參數呢?這個也能夠直接用閉包

function someFunction(words){
    return function(){
        alert(words);
    };
}
var link = document.getElementById("myLink");
link.onclick = function(){
    var saySomething = someFunction("你點擊了點擊個人超連接!");
    setTimeout(saySomething,1000);
};

在動態執行環境中,數據實時地發生變化,爲了保持這些非持久型變量的值,咱們用閉包這種載體來存儲這些動態數據。這就是閉包的做用。也就說遇到須要存儲動態變化的數據或將被回收的數據時,咱們能夠經過外面再包裹一層函數造成閉包來解決。

相關文章
相關標籤/搜索