閉包那一點事

閉包要解決的問題

提供一種間接的方式可以訪問到函數內部的數據(變量)javascript

貼吧上一個閉包有什麼用的問題和回答java

閉包的基本模式

在函數內部建立函數(內部函數),在這個內部函數中,能夠操做外部函數中的變量
01 在函數(外部)中建立函數(內部函數),在該函數(內部函數)中操做外部函數中的變量
02 在外部函數中,把內部函數做爲返回值返回
03 調用外部函數,並接收其返回值(是一個函數)
04 調用接收到的返回值(內部函數),來間接的操做外部函數中的變量segmentfault

關注點1(爲何直接返回num不算是標準閉包?)

function inner(){
    var num = 10;
    return num;
  };
  var fn1 = inner();
  var fn2 = inner();
  console.log(fn1);  //10
  console.log(fn2);  //10

其實每個函數體就是一個閉包,但該博文所指的閉包並無這麼簡單。這裏的inner儘管也能從外部讀取inner函數的局部變量,但fn1,fn2是經過各開闢一個內存空間去存儲num的值,就至關於fn1=new Number(10),fn2 = new Number(10),這樣不能修改inner裏面的變量值,並不能達到獲取內部變量和修改內部變量,其次咱們想要獲得的是將inner內部的變量暴露出外部使用,這明顯fn1,fn2各玩各的。數組

閉包的完整認識關係

需求:想要在函外面(另一個函數中)訪問某一個函數內部的局部變量
閉包:在函數內部使用函數
閉包鏈式做用域:一個子對象能夠沿着他的父對象訪問父對象的全部變量,反之,則不行多線程

function  sum() {
        var a = 10;
        function  showMessage() {

            //根據鏈式做用域在這個函數內部能夠訪問變量a
            alert(a);
        }

        return showMessage;

    }

    //調用sum
 var showMes =  sum();//調用完成後,正常狀況局部變量應該被銷燬,但最終沒有
 showMes();

    //經過上面的使用,局部變量a沒有銷燬
    //閉包的做用:1.延長局部變量的生命週期
    //若是有不少局部變量,那麼延長他們的生命週期,會大量佔用內存,慎重使用閉包

閉包獲取內部數據及修改內部數據

1、獲取單個數據(考慮賦值)併發

function func() {
        var num = 123;
        return function (a) {
            if (a !== undefined)  //容錯操做
            {
                num = a;
            }
                return num;
        }
    }

    var f1 = func();
    var x = f1(456);   //經過參數修改內部變量
    var y = f1();
    console.log(x);    //456
    console.log(y);    //123

2、獲取多個數據兩種方式(返回數組或對象)

function func() {
        var name = "張學友";
        var age = 40;
        return [
                function getName() {
                    return name;
                },
                function getAge() {
                    return age;
                }
        ]
    }

    var foo = func();
    console.log(foo[0]());      //張學友
    console.log(foo[1]());      //40

說明:上面的代碼可以知足返回多個變量值的需求,可是要數組操做的方式並不常見,且和使用習慣不符合。函數

利用對象返回並設置對個變量值spa

function foo() {
        var name = "張學友";
        var age = 45;

        return {
            getName:function () {
                return name;
            },
            getAge:function () {
                return age;
            },
            setName:function (nameValue) {
                name = nameValue;
            },
            setAge:function (ageValue) {
                age = ageValue;
            }
        }
    }

    var func = foo();
    console.log(func.getName());        //張學友
    console.log(func.getAge());         //45

    func.setName("張三");
    func.setAge(30);
    console.log(func.getName());        //張三
    console.log(func.getAge());         //30

 

儘可能少用全局變量和閉包

1、在函數中使用var操做符定義一個變量,那麼當這個函數執行完畢以後,這個變量也會被銷燬(也有的狀況下不會,好比閉包,後面會說明),而全局變量會一直存在。因此在咱們寫代碼時,儘可能少的使用全局變量,濫用全局變量,簡直就是一個會使人噁心的習慣,由於它會帶來不少沒必要要的麻煩。線程

  • 1:變量過多,命名麻煩
  • 2:局部變量,忘記使用var定義,修改了全局變量,這樣的錯誤對於代碼的維護簡直是噩夢
  • 3:全局變量會在頁面卸載前一直存在,損耗沒必要要的內存。

有兩個網址都是第一個解釋做用域和閉包,第二個解釋閉包,做用域鏈,和垃圾回收機制

***************************************************************************

在說閉包經常使用場景時前先知道javascript的線程任務優先級,對下面理解有很大幫助

進程和線程:

進程指的是系統中正在運行的一個應用程序。
線程:一個進程中能夠有一個或多個線程,線程是CPU調度的最小單位,是真正執行任務的。
多線程:一箇中可能有多條線程,多條線程之間併發的執行多個不一樣的任務。
單線程:一個進程中只有一條線程,即同一時間只能執行一個操做,只能幹一件事情。

javascript是單線程的:

js中的線程主要處理三塊任務:(由上到下優先級處理)
01 渲染任務
02 js的代碼執行任務
03 js中的事件處理任務(如setTimeOut方法)

 

那麼來了究竟閉包經常使用什麼地方呢

如下閉包經常使用場景

一、DOM操做

//函數定義和調用一塊兒使用,
    //形式:(function sum(){})(),簡化成(函數的定義)(函數的調用);
    var btns = document.getElementsByTagName('button');
    //遍歷按鈕
    for(var i= 0;i < btns.length;i++){
//        btns[i].onclick = (function (a) {
//            alert(a);
//        })(i)
        (function (a) {
         btns[a].onclick = function () {
                alert(a);
            }

     })(i) ;
    }


    當點擊按鈕的時候,須要輸出i值,那麼這個函數首先回會去尋找本身函數內部有沒有對應的i值,若是有就直接訪問這個i值,若是沒有就要沿着閉包鏈式做用域去他的父對象中尋找對應變量i, 因此會去for循環中尋找i值,可是注意函數執行實在按鈕點擊的時候才觸發,按鈕點擊是一個延遲操做,當按鈕點擊的時候for中的i值執行已經完畢,並且值是循環長度,因此咱們獲取的值5;(註釋部分)
    經過閉包每一次循環都會保存i值,在彈出i值所在的函數中放置一個變量,那麼就能夠實現輸出0-4;

*************************************************************************************************

二、定時器中閉包的使用

for(var i = 0; i < 4; ++i){
    // (function(a){
    //   setInterval(function(){
    //     console.log(a);
    //   },0)
    // })(i)
    setInterval((function (a) {
            console.log(a)
        })(i),0);

  }

註釋部分是出現無序的數字,而且定時器沒有中止,反之第二種寫法能夠得到想要效果

其餘:函數名函數只能函數體內調用:

var a = function b(){
      console.log(1);
};

a();  //1
b();  //not defined   (只能在局部使用,相似閉包)
var a = function b(){
  	console.log(1);
  	console.log(b); 
}();		// 1  function b(){...}
相關文章
相關標籤/搜索