1. 程式人生 > >【轉載】解決閉包的9種方法

【轉載】解決閉包的9種方法

1.正確的說,應該是指一個閉包域,每當聲明瞭一個函式,它就產生了一個閉包域(可以解釋為每個函式都有自己的函式棧),每個閉包域(Function 物件)都有一個 function scope(不是屬性),function scope內預設有個名為Global的全域性引用(有了這個引用,就可以直接呼叫 Global 的屬性或方法)

2.凡是在閉包域內宣告的變數或方法,外部無法直接訪問

3.閉包域可以訪問外部的變數或方法 
(上圖為 chrome 下 debug 環境)

當在一個閉包域內包含另一個閉包域時(簡單的說就是在一個函式內有另一個函式,當然這個內部函式的生命週期是依附於外部函式的), 此時,若子閉包域(內部的閉包域,內部函式)使用了父閉包域(外部閉包域,外部函式)的私有變數(在父閉包域中宣告的變數,父閉包域的外部空間無法直接訪問,但子閉包域可以訪問),子閉包域即當前的子函式的 function scope 會產生一個 closure 物件屬性,這個物件屬性內包含的是子閉包域對父閉包域的所有引用(只要子閉包域(內部函式)還存活,其父閉包域(外部函式)就依舊存活),倘若在父閉包域存活期間對其私有變數內容進行修改,則對這些父閉包域的私有變數進行引用的子閉包域中 function scope 的 closure 物件屬性的內容也會發生變化,因為這只是引用.

舉例:

<!DOCTYPE html>

<htmllang="en">

<head>

<metacharset="UTF-8">

<title></title>

</head>

<body>

<scripttype="text/javascript"charset="utf-8">

//函式 a 有一個私有變數 p 和一個內部函式 innerA

function a(){//外部閉包域 ,一個名為 a 的 Function 物件

var p =0;//私有變數 p

var innerA =function(){//內部閉包域 ,一個名為 innerA 的 Function 物件

               console.log(p);//對外部閉包域的私有變數進行了引用,故 innerA 物件的 function scope 會產生一個名為 closure 的物件屬性,closure 物件內含有一個名為 p 的引用

}

           innerA();//輸出0

           p++;

           innerA();//輸出1

}

       a();

</script>

</body>

</html>

結果如下:

第一次呼叫innerA


第二次呼叫 innerA


控制檯輸出


回到主題 面試經典問題

<!DOCTYPE html>

<htmllang="en">

<head>

<metacharset="UTF-8">

<title></title>

<scripttype="text/javascript">

//面試經典問題:

function onMyLoad(){

/*

           丟擲問題:

               此題的目的是想每次點選對應目標時彈出對應的數字下標 0~4,但實際是無論點選哪個目標都會彈出數字5

           問題所在:

               arr 中的每一項的 onclick 均為一個函式例項(Function 物件),這個函式例項也產生了一個閉包域,

               這個閉包域引用了外部閉包域的變數,其 function scope 的 closure 物件有個名為 i 的引用,

               外部閉包域的私有變數內容發生變化,內部閉包域得到的值自然會發生改變

           */

var arr = document.getElementsByTagName("p");

for(var i =0; i < arr.length;i++){

               arr[i].onclick =function(){

                   alert(i);

}

}

}

</script>

</head>

<bodyonload="onMyLoad()">

<p>產品一</p>

<p>產品二</p>

<p>產品三</p>

<p>產品四</p>

<p>產品五</p>

</body>

</html>

解決辦法:

解決辦法一

/*

解決思路:

   增加若干個對應的閉包域空間(這裡採用的是匿名函式),專門用來儲存原先需要引用的內容(下標),不過只限於基本型別(基本型別值傳遞,物件型別引用傳遞)

*/

for(var i =0;i<arr.length;i++){

//宣告一個匿名函式,若傳進來的是基本型別則為值傳遞,故不會對實參產生影響,

//該函式物件有一個本地私有變數arg(形參) ,該函式的 function scope 的 closure 物件屬性有兩個引用,一個是 arr,一個是 i

//儘管引用 i 的值隨外部改變 ,但本地私有變數(形參) arg 不會受影響,其值在一開始被呼叫的時候就決定了.

(function(arg){

       arr[i].onclick =function(){//onclick函式例項的 function scope 的 closure 物件屬性有一個引用 arg,

           alert(arg);//只要 外部空間的 arg 不變,這裡的引用值當然不會改變

}

})(i);//立刻執行該匿名函式,傳遞下標 i(實參)

}

解決辦法二

/*

解決思路:

   將下標作為物件屬性(name:"i",value:i的值)新增到每個陣列項(p物件)中

*/

for(var i =0;i<arr.length;i++){