****閉包****
1.閉包應(yīng)該是指一個閉包域瓷产,每當(dāng)聲明一個函數(shù)時就產(chǎn)生了一個閉包域——這里可以解釋為每個函數(shù)都有自己的函數(shù)棧——每個閉包域(Function對象)都有一個function scope(這里的function scope不是屬性)鸟蜡,function scope內(nèi)默認(rèn)有一個Globe的全局引用,這個引用可以直接調(diào)用Globe的屬性或者方法包各。
2.外部無法訪問在閉包域內(nèi)聲明的變量或者方法那伐,閉包域可以訪問外部的變量或者方法。
3.當(dāng)一個閉包域內(nèi)包含另一個閉包域時(一個函數(shù)內(nèi)有另一個函數(shù)应又,內(nèi)部函數(shù)的生命周期依賴于外部函數(shù))宙项,若子閉包域(內(nèi)部函數(shù))使用了父閉包域(外部函數(shù))的私有變量(在父閉包域中聲明的變量,父閉包域的外部空間無法直接訪問株扛,但子閉包域可以訪問)尤筐,子閉包域(當(dāng)前的子函數(shù))的function scope會產(chǎn)生一個closure對象屬性,這個對象屬性內(nèi)包含的是子閉包域?qū)Ω搁]包域的所有引用(只要子閉包域還存活洞就,則父閉包域就依舊存活)如果在父閉包域存活期間對私有變量進(jìn)行修改盆繁,那么子閉包域?qū)Ω搁]包域私有變量的引用中function scope的closure對象屬性的內(nèi)容也會發(fā)生變化。
以下代碼中原本是想每次點擊對應(yīng)的目標(biāo)彈出對應(yīng)的數(shù)字下標(biāo)0-4旬蟋,但實際上無論點擊哪個目標(biāo)都會彈出數(shù)字5
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
<script type="text/javascript">
function onMyLoad(){
var arr = document.getElementsByTagName("p");
for(var i = 0; i < arr.length; i++){
arr[i].onclick = function(){
alert(i);
}
}
}
</script>
</head>
<body onload="onMyLoad()">
<p>0</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
</body>
</html>
上面點擊時油昂,arr中每一項的onclick均為一個函數(shù)實例(Function對象),這個函數(shù)實例產(chǎn)生一個閉包域倾贰,這個閉包域引用了外部閉包域的變量秕狰,其function scope的closure對象有個名為i的引用,外部閉包域的私有變量內(nèi)容發(fā)生變化時內(nèi)部閉包得到的值就會改變躁染。
1)上面代碼出現(xiàn)的問題第一種解決辦法:增加若干個對應(yīng)的閉包域空間(采用匿名函數(shù)實現(xiàn))專門用來存儲原先需要引用的內(nèi)容(下標(biāo)值)鸣哀,只限于基本類型(基本類型值傳遞,對象類型引用傳遞)
//聲明一個匿名函數(shù)吞彤,若傳進(jìn)來的是基本類型則為值傳遞我衬,不會對實參影響
for(var i = 0; i<arr.length; i++){
(function (arg){//這個函數(shù)對象有一個本地私有變量arg(形參)叹放,該函數(shù)的function scope的closure對象屬性有兩個引用:arr和i。i的值隨外部改變挠羔,但是本地的私有變量(形參)arg不會受影響井仰,其值在一開始被調(diào)用時就決定了
arr[i].onclick = function () {//onclick函數(shù)實例的function scope的closure對象屬性有一個引用arg
alert(arg);//只要外部空間的arg不變,這里的引用值就不會改變
}
})(i);//立即執(zhí)行匿名函數(shù)破加,傳遞下標(biāo)i(實參)
}
2)第二種解決方法:將下標(biāo)作為對象屬性(name:"i",value:i的值)添加到每個數(shù)組項(p對象)中
for(var i=0; i<arr.length; i++){
//為當(dāng)前數(shù)組項(當(dāng)前p對象)添加一個名為i的屬性俱恶,值為循環(huán)體i變量的值
//此時當(dāng)前p對象的i屬性并不是對循環(huán)體的i變量的引用,而是一個獨立p對象的屬性范舀,屬性值在聲明的時候就確定了
arr[i].i = i;
arr[i].onclick = function (){
alert(this.i);
}
}
3)第三種解決方法:增加若干個對應(yīng)的閉包域空間用來存儲下標(biāo)合是。新增的匿名閉包空間內(nèi)完成事件綁定。
//綁定的函數(shù)中的function scope中的closure對象的引用arg是指向?qū)⑵浞祷氐哪涿瘮?shù)的私有變量arg
for(var i = 0; i<arr.length; i++){
arr[i].onclick = (function(arg){
return function () {
alert(arg);
}
})(i);
}
4)第四種解決方法:跟第一種方法類似
for(var i = 0; i<arr.length; i++){
(function(){
var temp = i;
arr[i].onclick = function (){
alert(temp);
}
})();
}
5)第五種解決方法:跟第三種方法和第四種方法類似
for(var i = 0; i<arr.length; i++){
arr[i].onclick = (function () {
var temp = i;
return function () {
alert(temp);
}
})();
}
6)第六種解決方法:將下標(biāo)添加為綁定函數(shù)的屬性
for(var i = 0; i<arr.length; i++){
(arr[i].onclick = function () {
alert(arguments.callee.i);//arguments參數(shù)對象
}).i = i;
}
7)第七中解決方法:通過new使用Function構(gòu)造函數(shù)創(chuàng)建Function實例锭环,傳入函數(shù)體的內(nèi)容是字符串聪全,所以Function得到的是一個字符串拷貝,而沒有得到i的引用辅辩。先獲取i.toString()然后與前后字符串拼接成一個新的字符串难礼,F(xiàn)unction對其方向解析成JS代碼。
for(var i = 0; i<arr.length; i++){
arr[i].onclick = new Function("alert("+i+");");//每new一個Function得到一個Function對象(一個函數(shù))玫锋,有自己的閉包域
}
8)第8種解決方法:直接通過Function返回一個函數(shù)蛾茉。第七種解決方法中使用了new,使用了new撩鹿,那么Function函數(shù)就被當(dāng)成構(gòu)造器可以用來構(gòu)造一個Function實例返回谦炬。這種方法中不使用new,將Function函數(shù)當(dāng)成一個函數(shù)三痰,傳入?yún)?shù)返回一個新函數(shù)吧寺。
//使用了new,F(xiàn)unction函數(shù)充當(dāng)構(gòu)造器散劫,由JS解析器產(chǎn)生一個新的對象稚机,構(gòu)造器內(nèi)的this指向該新對象
//不使用new,F(xiàn)unction函數(shù)依舊是函數(shù)获搏,由函數(shù)內(nèi)部自己產(chǎn)生一個實例返回
for(var i = 0; i<arr.length; i++){
arr[i].onclick = Function("alert("+i+");");
}
9)第九種解決方法:使用ES6的let關(guān)鍵字(有些瀏覽器不支持)
"use strict";
var arr = document.getElementsByTagName("p");
for(var i = 0; i<arr.length; i++){
let j = i;//塊級變量
arr[i].onclick = function () {
alert(j);
}
}