說到閉包,大家可能會比較頭疼,也是面試題比較常考的。接下來就形象的來說一些閉包。
程序運行的法則——作用域
說起閉包,首先肯定要說的是作用域,這裏也簡單說一下。
我們平時在寫代碼的時候經常會遇到’“xxx” is not defined’,那究竟程序是怎麼判斷是不是’defined’的呢?這時候肯定要制定一些規則,程序怎麼去找這些函數、變量。
首先作用域就是這些函數、變量可用的範圍。也就是說我想要用這些變量、函數的安全範圍,超出作用域範圍外使用這些定義的量就會是“undefined”。那我在用這個定義的變量的時候程序是怎麼判斷它有沒有在作用域範圍內呢。這就是作用域鏈的定義。
當我們在使用一個變量或者函數時,程序會首先在本層函數裏找這個變量或者函數,當沒有時,就會向外層去尋找這個變量、函數,當外層也沒有時,就再向外層的外層,以此類推,直到找到最外層也就是全局變量或者函數那裏,如果還沒有,那就是可以判斷這個函數、變量是“undefined”的了。
什麼是閉包
既然有這些規則在這,但是有的時候,我們會說,那我有需求想要訪問不在我作用域範圍內的函數、或者變量怎麼辦。其實這就是閉包了。
閉包:簡單來說其實就是:被包在函數內部的變量,在外部被操作到了。
按照我們說的作用域來說,就是在作用域外訪問到了某個變量。
百度上的解釋是:閉包就是能夠讀取其他函數內部變量的函數。
其實簡單理解來說也就是:閉包就是將函數內部和函數外部連接起來的橋樑。
閉包形成的條件
那我們怎麼做才能形成閉包——也就是訪問不在作用域內的變量呢?
這裏有形成閉包的三個條件:
- 外層函數中要有局部變量;
- 內層函數中要操作外層函數的局部變量;
- 外層函數將內層函數返回給外部變量。
只要滿足這三個條件,就能形成閉包。
舉個例子:
// 外層函數
function bibao(){
// 局部變量
var a = 10;
// 內層函數
var neibu = function(){
//操作局部變量
a++;
console.log(a);
}
// 返回內層函數
return neibu;
}
var jeishou = bibao();
jeishou(); //打印出來是11
至此我們在外層返回到了局部變量“a”,便形成了閉包。
閉包的經典案例
案例一:點擊按鈕顯示按鈕的index
<button>我是按鈕</button>
<button>我是按鈕</button>
<button>我是按鈕</button>
<button>我是按鈕</button>
<button>我是按鈕</button>
<button>我是按鈕</button>
<button>我是按鈕</button>
<button>我是按鈕</button>
<button>我是按鈕</button>
var btns = document.querySelectorAll('button');
for(var i = 0;i<btns.length;i++){
// 自調用匿名函數 形參i相當於局部變量
(function(i){
btns[i].onclick = function(){
alert(i);
}
})(i)
}
案例二:點擊不同按鈕,div字號變化
<button>點擊文字變成100px</button>
<button>點擊文字變成200px</button>
<button>點擊文字變成300px</button>
<div>文字</div>
var div = document.querySelector("div");
btns[0].onclick = setSize(100);
btns[1].onclick = setSize(200);
btns[2].onclick = setSize(300);
function setSize(num){
var fn = function(){
div.style.fontSize = num + "px";
}
return fn;
}
戲說閉包
之前聽過一個老師講的閉包,比較形象一點,可能會加深記憶。
可以把外層函數比作是一個公寓,把變量比作藏於公寓中的“三兒",然後內層函數是管家。當整個函數賦值給一個朝陽羣衆,這樣的意義便是:“藏於別墅裏的三兒被朝陽羣衆通過管家發現了”
例子:
function bieshu(){
var san = 100;
function guanJia(){
san++;
console.log(san);
}
return guanJia;
}
var chaoyangqunzong = bieshu();
chaoyangqunzong(); //101
希望這樣可以加深你的記憶,讓你對閉包理解的更加清晰一些。