首頁 >web前端 >js教程 >基於javascript 閉包基礎分享_基礎知識

基於javascript 閉包基礎分享_基礎知識

WBOY
WBOY原創
2016-05-16 17:29:11968瀏覽

如果對作用域,函數為獨立的對象這樣的基本概念理解較好的話,理解閉包的概念並在實際的編程實踐中應用則頗有水到渠成之感。

在DOM的事件處理方面,大多數程式設計師甚至自己已經在使用閉包了而不自知,在這種情況下,對於瀏覽器中內嵌的JavaScript引擎的bug可能造成內存洩漏這一問題姑且不論,就是程式設計師自己調試也常常會一頭霧水。
用簡單的語句來描述JavaScript中的閉包的概念:由於JavaScript中,函數是對象,對像是屬性的集合,而屬性的值又可以是對象,則在函​​數內定義函數成為理所當然,如果在函數func內部宣告函數inner,然後在函數外部呼叫inner,這個過程即產生了一個閉包。
閉包的特性:
我們先來看一個例子,如果不了解JavaScript的特性,很難找到原因:

複製碼 程式碼如下:

var outter = [];
        function clouseTest() {  two", "three", "four"];
            for (var i = 0; i               x.no = i;
                x.text            x.text          print(i);
                }
          }
        }
        //呼叫這個函數
        clouseTest(); print(outter[2].invoke());
        print(outter[3].invoke());



運作的結果如何呢?許多初學者可能會得出這樣的答案:

0
1
2
3
然而,運行這個程序,得到的結果為:
4
4
4
4
其實,在每次迭代的時候,這樣的語句x.invoke = function(){print(i);}並沒有被執行,只是建構了一個函數體為”print(i);”的函數對象,如此而已。而當i=4時,迭代停止,外部函數返回,當再去調用outter[0].invoke()時,i的值依舊為4,因此outter數組中的每一個元素的invoke都返回i的值:4。如何解決這問題呢?我們可以宣告一個匿名函數,並立即執行它:


複製程式碼


程式碼如下:

var outter = [];
        function clouseTest2() {
                 for (var i = 0; i                 var x = {};
              x.text = array[i];
                x.invoke = function (no) {
                    return function () {
                         }
              ;
            }
}
        clouseTest2();
   


這個例子中,我們為x.invoke賦值的時候,先執行一個函數的函數,然後立即執行一個函數的函數,然後立即執行一個函數的函數之,這樣,x.invoke的每一次迭代器時相當與執行這樣的語句:



複製程式碼
程式碼如下://x == 0x.invoke = function(){print(0);}
//x == 1
x.invoke = function() {print(1);}
//x == 2
x.invoke = function(){print(2);}
//x == 3
x.invoke = function (){print(3);}


這樣就可以得到正確結果了。閉包允許你引用存在於外部函數中的變數。然而,它並不是使用該變數創建時的值,相反,它使用外部函數中該變數最後的值。

閉包的用途:

現在,閉包的概念已經清晰了,我們來看看閉包的用途。事實上,透過使用閉包,我們可以做很多事情。例如模擬物件導向的程式碼風格;更優雅,更簡潔的表達出程式碼;在某些方面提升程式碼的執行效率。

快取:

再來看一個例子,設想我們有一個處理過程很耗時的函數對象,每次呼叫都會花費很長時間,那麼我們就需要將計算出來的值儲存起來,當呼叫這個函數的時候,首先在快取中查找,如果找不到,則進行計算,然後更新快取並返回值,如果找到了,直接返回查找到的值即可。 閉包正是可以做到這一點,因為它不會釋放外部的引用,從而函數內部的值可以得以保留。



複製程式碼
程式碼如下:

var CachedSearchBox = (function () {
            var cache = {},
         return {
                attachSearchBox: function (dsid) {
    (dsid in cache) {//如果結果在快取中
                           }
                    var fsb = document.getElementById(dsid);///新
                    cache[dsid] = fsb;//更新快取
     的大小                        delete cache[count.shift()];
                    }
                 },
                clearSearchBox: function (dsid) {
                         cache[dsid].clearSelection();
        🎜>                }
            } Box.attachSearchBox("input1");
        //alert(obj1);
        var obj2 = CachedSearchBox.attachSearchBox      var obj2 = CachedSearchBox.attachSearchBox      var obj2 = CachedSearchBoxBox ("input1");



實現封裝:




複製代碼
程式碼如下:
             name = newName;
       }
    }
}();

print(person.name);//直接訪問,結果為undefined
print(person.getName());
person.setName("jack");
print(person.getName());



得到結果如下:

undefined
default
jack

閉包的另一個重要用途是實現面向對像中的對象,傳統的對象語言都提供類的模板機制,這樣不同的對象(類的實例)擁有獨立的成員及狀態,互不干涉。雖然JavaScript中沒有類別這樣的機制,但透過使用閉包,我們可以模擬出這樣的機制。還是以上邊的範例來講:


複製程式碼

程式碼如下:

function Person(){
var name = "default";

return {
getName : function(){
return name;
},
setName: function(newName){
name = newName;
}
}
};
var john = person();
print(john.getName()) ;
john.setName("john");
print(john.getName());

var jack = person();
print(jack.getName());
jack.setName("jack");
print(jack.getName());

実行結果は次のとおりです:
default
john
デフォルト
ジャック

JavaScript クロージャが注意すべき問題:
1. メモリ リーク:
インタープリタ自体の欠陥により、さまざまな JavaScript インタープリタ実装で、クロージャはメモリ リークを引き起こす可能性があります。メモリ リークは深刻な問題であり、ブラウザの応答速度に重大な影響を及ぼし、ユーザー エクスペリエンスを低下させ、さらにはブラウザが応答しなくなることもあります。 JavaScript インタプリタにはすべて、参照カウントの形式を採用するガベージ コレクション メカニズムがあり、オブジェクトの参照カウントがゼロの場合、このプロセスは自動的にリサイクルされます。ただし、クロージャの概念では、将来のある時点でローカル変数を使用する必要がある可能性があるため、このプロセスは複雑になります。循環参照が発生した場合、ガベージ コレクション メカニズムはこれらの外部参照を処理しません。 、オブジェクト A が B を参照し、B が C を参照し、C が A を参照すると、ガベージ コレクション メカニズムはその参照カウントがゼロではないと判断し、メモリ リークが発生します。

2. コンテキスト参照:

コードをコピーします コードは次のとおりです。 🎜>
$(function(){
var con = $("div#panel");
this.id = "content";
con.click(function() {
alert(this.id);//panel
});
});

ここでのalert(this.id)はどの値を指しますか?多くの開発者は、クロージャの概念に基づいて誤った判断を下す可能性があります:

content
その理由は、this.id にクリック コールバックに表示される値が割り当てられ、クロージャが参照することを形成するためです。 .id なので、戻り値は content です。ただし、実際には、このアラートは「パネル」をポップアップします。これは、クロージャがローカル変数を参照できるにもかかわらず、呼び出し元のオブジェクトの存在によって状況が少し微妙になるためです。クロージャーが呼び出されるとき (このパネルのクリック イベントが発生するとき)、ここでは jQuery オブジェクト con を参照します。匿名関数の this.id = "content" は、匿名関数自体に対して実行される操作です。 this が参照する 2 つは、同じオブジェクトを参照していません。

イベント ハンドラーでこの値にアクセスしたい場合は、いくつかの変更を加える必要があります:
コードをコピー コードは次のとおりです:
$(function(){
var con = $("div#panel");
this.id = "content" ;
var self = this;
con.click(function(){
alert(self.id);//content
});
});

このように、イベント処理関数に保存するのは this ではなく、外部ローカル変数 self への参照です。このテクニックには多くの実際的な応用例があり、次の章で詳しく説明します。他の命令型言語での「クロージャ」の説明や、実際のプロジェクトでのクロージャの適用など、クロージャについては第 9 章で詳しく説明します。

添付: 私のレベルが限られているため、記事に省略や誤りがあることは避けられません。また、文言自体が不適切である可能性もあります。タイムリーな修正や提案を歓迎します。この記事は他の人たちにインスピレーションを与えるためのものです、皆さんに感謝します!
陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn