官方對閉包的解釋是:一個擁有許多變量和綁定了這些變量的環境的表達式(通常是一個函數),因而這些變量也是該表達式的一部分。
一、javascript閉包的特點:
1.作為一個函數變量的一個引用,當函數返回時,其處于激活狀態。
2.一個閉包就是當一個函數返回時,一個沒有釋放資源的棧區。
簡單的說,javascript允許使用內部函數---即函數定義和函數表達式位于另一個函數的函數體內。而且,這些內部函數可以訪問它們所在的外部函數中聲明的所有局部變量、參數和聲明的其他內部函數。當其中一個這樣的內部函數在包含它們的外部函數之外被調用時,就會形成閉包。
二、javascript閉包的原理
閉包是指有權訪問另一個函數作用域中的變量的函數。根據下面的代碼示例來理解什么是閉包,在add函數內部的匿名函數中,訪問到外部函數的變量outerArg,在執行add(10)之后外部函數返回了,并且將內部的匿名函數賦值給了變量addTen,此時通過addTen調用函數,依然可以訪問到outerArg,也就是10。這個閉包中的變量,只能通過調用addTen函數訪問,無法通過其他渠道訪問到,下面代碼最后一行通過輸出屬性的方式嘗試訪問結果是輸出undefined。
outerArg是屬于add函數的作用域中的變量,addTen有權訪問add函數作用域中的變量,因此addTen是一個閉包。閉包產生的本質是:在一個函數(外部函數)內部定義的函數(內部函數)會將外部函數作用域中的活動對象添加到自己的作用域鏈中,下面代碼中inner函數將add函數的outerArg添加到自己的作用域鏈上。在add函數執行完之后,其執行環境會被銷毀,但由于inner函數還在引用outerArg,所以outerArg不會被銷毀,依然保留在inner函數的作用域鏈中。直到inner函數(addTen函數)被銷毀之后,outerArg才會跟著其作用域鏈一起被銷毀。由于閉包變量是位于作用域鏈上,因此必須調用閉包函數進入其作用域之后才能訪問到閉包變量。
function add(outerArg) {
function inner(innerArg) {
return innerArg + outerArg;
}
return inner;
}
var addTen = add(10);
console.log(addTen(1)); // 輸出11
console.log(addTen(2)); // 輸出12
console.log(addTen(3)); // 輸出13
console.log(addTen.outerArg); // undefined123456789101112
三、javascript閉包的應用
javascript閉包的應用1:
這個是我在用js模擬排序算法過程遇到的問題。我要輸出每一次插入排序后的數組,如果在循環中寫成
setTimeout(function() { $(“proc”).innerHTML += arr + “《br/》”; }, i * 500);
會發現每次輸出的都是最終排好序的數組,因為arr數組不會為你保留每次排序的狀態值。為了保存會不斷發生變化的數組值,我們用外面包裹一層函數來實現閉包,用閉包存儲這個動態數據。下面用了2種方式實現閉包,一種是用參數存儲數組的值,一種是用臨時變量存儲,后者必須要深拷貝。所有要通過閉包存儲非持久型變量,均可以用臨時變量或參數兩種方式實現。
《!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”》
《html xmlns=“http://www.w3.org/1999/xhtml”》
《head》
《title》《/title》
《script type=“text/javascript”》《!--
var arr = [4, 5, 6, 8, 7, 9, 3, 2, 1, 0];
var $ = function(id) { return document.getElementById(id); }
var Sort = {
Insert: function() {
for (var i = 1; i 《 arr.length; i++) {
for (var j = 0; j 《 i; j++) {
if (arr[i] 《 arr[j]) {
arr[i] = [arr[j], arr[j] = arr[i]][0];
}
}
setTimeout((function() {
var m = [];
for (var j = 0; j 《 arr.length; j++) {
m[j] = arr[j];
}
return function() {
$(“proc”).innerHTML += m + “《br /》”;
}
})(), i * 500);
//or 寫成下面這樣也可以
/*
setTimeout((function(m) {
return function() {
$(“proc”).innerHTML += m + “《br /》”;
}
})(arr.join(“,”)), i * 500);
*/
}
return arr;
}
}
// --》《/script》
《/head》
《body》
《div》
var a = [4, 5, 6, 8, 7, 9, 3, 2, 1, 0];《/div》
《div》
《input type=“button” value=“插入排序” onclick=“Sort.Insert();” /》
《/div》
Proc:《br /》
《div id=“proc”》
《/div》
《/body》
《/html》
javascript閉包的應用2:
這個是無憂上的例子,為每個《li》結點綁定click事件彈出循環的索引值。起初寫成
id.onclick = function(){ alert(i); } id.onclick = function(){alert(i);}
發現最終彈出的都是4,而不是想要的 1、2、3,因為循環完畢后i值變成了4。為了保存i的值,同樣我們用閉包實現:
《!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”》
《html xmlns=“http://www.w3.org/1999/xhtml”》
《head》
《title》《/title》
《script type=“text/javascript”》《!--
window.onload = function() {
for (var i = 1; i 《 4; i++) {
var id = document.getElementById(“a” + i);
id.onclick = (function(i) {
return function() {
alert(i);
}
})(i);
}
}
// --》《/script》
《/head》
《body》
《ul》
《li id=“a1”》aa《/li》
《li id=“a2”》aa《/li》
《li id=“a3”》aa《/li》
《/ul》
《/body》
《/html》
(ps:var a = (function(){})(); 與 var a =new function(){}效果是一樣的,均表示自執行函數。)
javascript閉包的應用3:
下面的code是緩存的應用,catchNameArr。在匿名函數的調用對象中保存catch的值,返回的對象由于被CachedBox變量引用導致匿名函數的調用對象不會被回收,從而保持了catch的值。可以通過CachedBox.getCatch(“regionId”);來操作,若找不到regionId則從后臺取,catchNameArr 主要是為了防止緩存過大。
《script type=“text/javascript”》
var CachedBox = (function() {
var cache = {}, catchNameArr = [], catchMax = 10000;
return {
getCatch: function(name) {
if (name in cache) {
return cache[name];
}
var value = GetDataFromBackend();
cache[name] = value;
catchNameArr.push(name);
this.clearOldCatch();
return value;
},
clearOldCatch: function() {
if (catchNameArr.length 》 catchMax) {
delete cache[catchNameArr.shift()];
}
}
};
})();
《/script》
同理,也可以用這種思想實現自增長的ID。
《script type=“text/javascript”》
var GetId = (function() {
var id = 0;
return function() {
return id++;
}
})();
var newId1 = GetId();
var newId2 = GetId();
《/script》
評論
查看更多