以前對於閉包的理解只是很膚淺的,只是浮於表面,此次深究了一下閉包,下面是我對閉包新的理解。javascript
引用高程裏的話 => 閉包就是有權訪問另外一個做用域中變量的函數,閉包是由函數以及建立該函數的詞法環境組成而成 記住,閉包是一個函數,廢話很少說,先來個例子:html
function makeFunc() {
var name = "Mozilla";
function displayName() {
alert(name);
}
return displayName;
}
var myFunc = makeFunc();
myFunc();
複製代碼
函數makeFunc裏面定義了一個函數,該函數引用了一個makeFunc內部的變量,而後返回該函數,myFunc變量承接了makeFunc返回的函數,也就是
function displayName() { alert(name); }
java
在下面執行該函數,咦!!!爲何函數裏面的name是從哪裏獲得的,爲何不報錯呢? 要搞明白其中的細節,必須從理解函數被調用的時候都會發生什麼入手node
當一個函數被調用的時候,會建立一個執行環境(執行上下文),以及相應的做用域,在函數執行完的時候該做用域鏈會被銷燬,函數裏面的變量也會被回收,可是閉包的狀況不是這樣的.. bash
仍是上面的例子,打印了一下myFunc函數,發現該函數的做用域鏈(scopes)保存着name變量,name變量並無被回收,因而咱們成功的在makeFunc函數外的做用域取到了name變量,myFunc就是閉包,咱們重溫一下閉包的特色=>閉包是由函數以及建立該函數的詞法環境組成而成. 這下概念就理解的更清楚了吧!<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<p id="help">Helpful notes will appear here</p>
<p>E-mail: <input type="text" id="email" name="email"></p>
<p>Name: <input type="text" id="name" name="name"></p>
<p>Age: <input type="text" id="age" name="age"></p>
<script type="text/javascript">
function showHelp(help) {
document.getElementById('help').innerHTML = help;
}
var helpText = [
{ 'id': 'email', 'help': 'Your e-mail address' },
{ 'id': 'name', 'help': 'Your full name' },
{ 'id': 'age', 'help': 'Your age (you must be over 16)' }
];
function returnHelp(i) {
return helpText[i];
}
function setupHelp() {
for (var i = 0; i < helpText.length; i++) {
var item = returnHelp(i);
document.getElementById(item.id).onfocus = function() {
showHelp(item.help);
}
}
}
setupHelp();
</script>
</body>
</html>
複製代碼
原本想實現一個點擊輸入框,顯示提示信息的功能,可是結果是顯示的全是age信息。。。爲何???閉包
緣由是賦值給onfouse的是閉包,這些閉包是由他們的函數定義和在setupHelp做用域中捕獲的環境所組成的 這三個閉包在循環中被建立,但他們共享同一個詞法做用域,在這個做用域中存在一個變量item, 點擊輸入框的時候,item已經變成helpText 的最後一個了,因此。。。 怎麼解決呢? 既然聚焦輸入框後的回調函數裏面的item變量引用的是共享的,因此只要把他變成非共享的就能夠了,app
function showHelp(help) {
document.getElementById('help').innerHTML = help;
}
var helpText = [
{ 'id': 'email', 'help': 'Your e-mail address' },
{ 'id': 'name', 'help': 'Your full name' },
{ 'id': 'age', 'help': 'Your age (you must be over 16)' }
];
function returnHelp(i) {
return helpText[i];
}
function setupHelp() {
for (var i = 0; i < helpText.length; i++) {
var item = returnHelp(i);
//經過這種方法把每個item變成具體的實例,而不是變量,避免每一次聚焦的時候執行函數裏面的變量都是指向同一個數
document.getElementById(item.id).onfocus = change(item);
}
}
function change(item) {
return function() {
showHelp(item.help);
}
}
setupHelp();
複製代碼
這樣把每一次循環的item傳給外面的函數,而後外邊函數返回具體的實例而非變量,這樣就解決了問題。異步
下面再來看一個經典的例子 :函數
<html>
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<script>
var nodes = document.getElementsByTagName('div');
for(var i = 0, len = nodes.length; i < len; i++){
nodes[i].onclick = function(){
alert(i);
}
};
</script>
</body>
</html
複製代碼
若是你認爲點擊每個div會返回對應的索引,那麼恭喜你,你掉坑了!!! 沒錯,一開始我也掉坑了,可能你們會感到很疑惑,爲何點徹底是5,由於點擊事件是異步的,點擊的時候變量 i 已經變成5了,因此點擊的時候彈出的全是5,怎麼解決呢,閉包來了ui
for(var i = 0, len = nodes.length; i < len; i++){
(function(i){
nodes[i].onclick = function(){
console.log(i);
}
})(i)
};
複製代碼
這段代碼是如何完美的解決問題的呢? 當咱們點擊的時候,回調函數裏面的 i 引用的是外面閉包的 i ,這樣問題就被完美的解決了。
在上面分析的過程當中也說到過,閉包不會被垃圾回收機制回收,會形成內存泄漏,記得閉包使用完手動把變量回收一下。