閉包:有權訪問另外一個函數做用域中的變量的函數;通常狀況就是在一個函數中包含另外一個函數。
從官方定義咱們知道閉包是一個函數,只不過這個函數有[超能力],能夠訪問到另外一個函數的做用域。
爲何說這個叫作[超能力]呢?
由於咱們知道函數做用域是獨立的、封閉的,外部的執行環境是訪問不了的,可是閉包具備這個能力和權限。
那閉包是怎樣的一個表現形式呢?
第一,閉包是一個函數,並且存在於另外一個函數當中
第二,閉包能夠訪問到父級函數的變量,且該變量不會銷燬安全
function person(){ var name = '有魚'; function cat(){ console.log(name); } return cat; } var per = person();// per的值就是return後的結果,即cat函數 per();// 有魚 per()就至關於cat() per();// 有魚 同上,並且變量name沒有銷燬,一直存在內存中,供函數cat調用 per();// 有魚
閉包的實現原理,實際上是利用了做用域鏈的特性,咱們都知道做用域鏈就是在當前執行環境下訪問某個變量時,若是不存在就一直向外層尋找,最終尋找到最外層也就是全局做用域,這樣就造成了一個鏈條。
例如:微信
var age = 18; function cat(){ age++; console.log(age);// cat函數內輸出age,該做用域沒有,則向外層尋找,結果找到了,輸出[19]; } cat();//19
看到這裏,你們都會說這不就是最簡單的函數和變量形式嗎?閉包在哪裏?別急,咱們接着往下看:
若是咱們再次調用時,結果會一直增長,也就變量age的值一直遞增。閉包
cat();//20 cat();//21 cat();//22
若是程序還有其餘函數,也須要用到age的值,則會受到影響,並且全局變量還容易被人修改,比較不安全,這就是全局變量容易污染的緣由,因此咱們必須解決變量污染問題,那就是把變量封裝到函數內,讓它成爲局部變量。函數
function person(){ var age = 18; function cat(){ age++; console.log(age); } return cat; } person();// 19 person();// 19
這裏又出現問題了,每次調用函數person,進入該做用域,變量age就會從新賦值爲18,因此cat的值一直是19;因此須要作一下調整:spa
var per = person();//per至關於函數cat per();// 19 即cat() 這樣每次調用不在通過age的初始值,這樣就能夠一直增長了 per();// 20 per();// 21
並且變量age在函數內部,不易修改和外泄,相對來講比較安全。code
做用1:隱藏變量,避免全局污染
做用2:能夠讀取函數內部的變量
同時閉包使用不當,優勢就變成了缺點:
缺點1:致使變量不會被垃圾回收機制回收,形成內存消耗
缺點2:不恰當的使用閉包可能會形成內存泄漏的問題
這裏簡單說一下,爲何使用閉包時變量不會被垃圾回收機制收銷燬呢,這裏須要瞭解一下JS垃圾回收機制;
JS規定在一個函數做用域內,程序執行完之後變量就會被銷燬,這樣可節省內存;使用閉包時,按照做用域鏈的特色,閉包(函數)外面的變量不會被銷燬,由於函數會一直被調用,因此一直存在,若是閉包使用過多會形成內存銷燬。
blog
需求:實現變量a 自增
一、經過全局變量,能夠實現,但會污染其餘程序圖片
var a = 10; function Add(){ a++; console.log(a); } Add(); Add(); Add();
二、定義一個局部變量,不污染全局,可是實現不了遞增內存
var a = 10; function Add2(){ var a = 10; a++; console.log(a); } Add2(); Add2(); Add2(); console.log(a);
三、經過閉包,能夠是函數內部局部變量遞增,不會影響所有變量,完美!!作用域
var a = 10; function Add3(){ var a = 10; return function(){ a++; return a; }; }; var cc = Add3(); console.log(cc()); console.log(cc()); console.log(cc()); console.log(a);