首先,先來講一下閉包的定義。什麼是閉包,閉包就是有權訪問另外一個函數做用域內變量的函數,如例:閉包
function abc(){ 函數
//這裏是abc()做用域
let a = 1;
function add(){性能
//這裏是add()做用域
a++;
console.log(a);//在這裏能夠訪問到a變量
};
add();
add();
add();
}spa
console.log(a) //在這裏會直接報錯,沒法訪問abc函數中的a變量
abc();//輸出2,3,4對象
要知道,js的每一個函數都是一個個獨立的密閉空間,它能夠獲取外界信息,可是外界卻沒法直接看到裏面的內容。將變量 a 放進小黑屋裏,只有add 函數能接觸到變量 a,並且在函數 abc 外定義同名的變量 a也是互不影響的,這就是所謂的加強「封裝性」。事件
看到了嗎,這就是閉包,如此簡單。內存
好,接下來,咱們來看看她是怎麼執行的作用域
function abc(){io
//這裏是abc()做用域
let a = 1;
function add(){console
//這裏是add()做用域
a++;
console.log(a);//在這裏能夠訪問到a變量
};
return add;
}
var c = abc();//這是函數表達式
c();//打印2
那咱們能夠看到,這裏的c()就至關因而abc();,好,那有人會問我不是在var c = abc();的時候就已經把abc函數賦值給c了嗎,難道c不等於abc()?,爲何下面一行我不能夠直接調用c,還要給他加括號呢?
是這樣的,在把abc函數賦值給c的時候,我真正賦值給他的是什麼?在abc函數中,你們能夠看一下,我返回了什麼,我返回的是add,也就是說var c = abc();這一行至關因而var c = add;在這一步個人操做的是什
麼,是否是進行了一次賦值,而我拿到的是一個標識符,一個函數名稱,它並無實際意義,在下一行在給了它()以後,它才具有了調用這個函數的資格。再看第二個例子:
function abc(){
//這裏是abc()做用域
let a = 1;
function add(){
//這裏是add()做用域
a++;
console.log(a);//在這裏能夠訪問到a變量
};
return add();//你們看下這裏返回的是什麼
}
A實例:
var c = abc();//這是函數表達式
c;//打印2
或者能夠這麼寫:
B實例:
var c = abc;//這是函數表達式
c();//打印2
你們能夠看一下這兩個例子,不一樣的地方在於返回的值,和賦給變量的值,在第二個例子中,我返回的是add(),因此,變量c就是add();在A實例中我賦給c的函數名稱和函數體,在下一行,我就直接調用c就可
以了,但在B實例中,我賦給c的是函數名稱,而沒有函數體,在下一行,我就必須加上,同時調用他,才能獲得返回值。
結論:在函數中真正重要的角色是什麼,是函數體,也就是這個();
閉包的優勢:
1.使用閉包代替全局變量
2.函數外或在其餘函數中訪問某一函數內部的參數
3.在函數執行以前爲要執行的函數提供具體參數
4.在函數執行以前爲函數提供只有在函數執行或引用時才能知道的具體參數
5.爲節點循環綁定click事件,在事件函數中使用當次循環的值或節點,而不是最後一次循環的值或節點
6.暫停執行
7.包裝相關功能
閉包的缺點:
1.內存消耗
一般來講,函數的活動對象會隨着執行期上下文一塊兒銷燬,可是,因爲閉包引用另一個函數的活動對象,所以這個活動對象沒法被銷燬,這意味着,閉包比通常的函數須要更多的內存消耗。解決方法是,在退出函數以前,將不使用的局部變量所有刪除。
2.性能問題
使用閉包時,會涉及到跨做用域訪問,每次訪問都會致使性能損失。所以在腳本中,最好當心使用閉包,它同時會涉及到內存和速度問題。不過咱們能夠經過把跨做用域變量存儲在局部變量中,而後直接訪問局部變量,來減輕對執行速度的影響。