簡單詳細講解js閉包(看完不懂你砍我!!!)

  《javascript高級程序設計》中閉包的概念:javascript

    閉包,實際上是一種語言特性,它是指的是程序設計語言中,容許將函數看做對象,而後能像在對象中的操做般在函數中定義實例(局部)變量,而這些變量能在函數中保存到函數的實例對象銷燬爲止,其它代碼塊能經過某種方式獲取這些實例(局部)變量的值並進行應用擴展。html

    咱們的理解:java

      其實閉包就是一個函數,一個外部函數經過調用函數並return返回出內部函數,這裏的內部函數就是一個閉包;此時在內部函數中是能夠訪問到外部函數的變量的;瀏覽器

    

    要想理解閉包,首先咱們要了解棧堆內存和做用域鏈;首先咱們來說解棧堆內存:閉包

       首先咱們來看個demo:函數

        

var a=1;
var obj={"name":"鹹魚"}

   上面簡單的兩句代碼,其實就是在內存中作了兩件事,效果圖以下:性能

  

  在js簡單實現深淺拷貝(http://www.javashuo.com/article/p-clxgdfpk-gq.html)一文中咱們知道基本數據類型是存儲在棧內存中的,引用數據類型是存儲在堆內存中的,其實上面的兩句代碼在內存中就是作了兩件事:1.首先在棧內存中開闢了一塊空間用來存放a的變量和值;2.在堆內存中開闢了一塊空間用來存儲obj的值,同時在將地址指向棧內存中的變量名objspa

  若是咱們在代碼下面再加上一句obj={"name":'張三"},這個時候咱們以前存儲name爲鹹魚的值也就是obj原來的值會被js中的垃圾回收機制回收掉,而後obj的值從新的指向{name:"張三"}這個值;設計

 

  做用域鏈code

   再來看一下這個例子:

var a = 1;
function fn(){
    var b = 2;
    function fn1(){
        console.log(b);//2
        console.log(a);//1
    }
    fn1();
}
fn(); 

 效果圖以下:

    

 

 

   1.var a=1;這個時候咱們是在全局執行環境的,瀏覽器的全局環境就是window做用域,咱們的window做用域中有a和fn;

  2.當咱們往下走到fn的時候,棧內存會開闢一塊新的執行環境,此時fn的執行環境中咱們有b和fn1;

  3.當咱們接着往下走到fn1的時候,這時棧內存一樣會開闢一塊新的執行環境,此時fn1的執行環境中是沒有任何變量數據的,可是咱們在fn1中輸出a、b,咱們都是能夠讀取到的;這是由於程序在讀取變量的時候是從內到外的開始讀的,是隨着fn1開始往上一層一層的查找,是這樣的執行順序(fn1 = > fn = > window),若是找到window中尚未讀取到變量,這時程序纔會報錯;

  固然在執行的過程當中,垃圾回收機制若是檢測到程序執行完了是會進行垃圾回收的,避免形成內存泄露等問題;就是說咱們的fn1裏面執行完以後fn1的做用域就會被銷燬,接着程序執行fn,fn執行完以後fn就會被銷燬;往上執行到全局的時候,整個程序就沒有了fn的做用域和fn1的做用域,只剩下瀏覽器的全局做用域window,這個時候window裏只剩a和fn;

 

   瞭解了上面的做用域鏈和棧內存和堆內存的知識以後,咱們來開始講解js閉包:

  

function outer() {
     var  a = '123'
    
    return function add(){
    //在這裏由於做用域的關係,add是能訪問到outer的全部變量的,可是outer是訪問不到add的變量;
    //因此思路一轉,把add的值做爲結果return出來變通實現outer外部函數訪問到了內部函數變量
// add就是一個閉包函數,由於他可以訪問到outer函數的做用域,add中沒有找到變量a,則會繼續往上層做用域找 console.log(a); } } var inner = outer() // 得到add閉包函數 inner() //"123"

 

    首先咱們能夠看到,在全局做用域下咱們是有一個outer函數的,outer做用域裏面有a和add,add做用域裏面執行控制檯輸出a的變量,此時這裏的add函數就造成了一個閉包,由於add函數裏面須要訪問到outer做用域下的a變量,而他們不處在同一個做用域中,因此二者相互牽引,須要輸出a,上面outer中的變量a就必須得在,做用域鏈查找到outer的時候找到a了,輸出a的時候,垃圾回收機制會認爲add尚未執行完成,由於此時的做用域鏈查找已經到了outer做用域下,因此不會清理a的內存空間;因此這就會帶來一個問題:若是咱們屢次的使用閉包,則會給咱們的程序帶來內存佔用過多,致使性能問題;

    函數內部能訪問全局變量是javascript語言的特殊之處,可是若是咱們想達到函數外部能訪問內部變量的時候,咱們就可使用閉包,這就是閉包給咱們帶來的便利;

    閉包的優缺點:

      優勢:

        1.能夠讀取函數內部的變量
        2.能夠避免全局污染

      缺點:

        1.閉包會致使變量不會被垃圾回收機制所清除,會大量消耗內存;

        2.不恰當的使用閉包可能會形成內存泄漏的問題;

總結:

  1.做用域鏈查找變量的方式是一層一層的往上查找,直到找到爲止,若是找到window全局做用域還未找到,就報undefined;

  2.嵌套函數中,由於不在同一做用域,正常狀況下內外部函數是訪問不到內部函數的,可是經過閉包能夠實現;

  3.儘量少的使用閉包,由於會形成內存消耗大以及有可能形成內存泄露(若是不須要的時候,不要隨便使用);

相關文章
相關標籤/搜索