{閉包 丨 定時(shí)器}

閉包

  • 定義
    「一個(gè)函數(shù)」+「訪問到的外部變量」= 閉包

  • 作用
    創(chuàng)建內(nèi)部變量,既不能被外部隨意修改,又可以通過指定的函數(shù)接口來操作。

  • 原理
    利用變量?jī)H在函數(shù)作用域內(nèi)部可用的特點(diǎn),用子函數(shù)來操作 / 暴露父作用域內(nèi)的變量,用父函數(shù)來保護(hù)變量不被外界直接修改。

  • 舉例
    function princess() { //從前有一位公主

           var adventures = [];     //她生活在一個(gè)充滿奇幻冒險(xiǎn)的世界里
    
           function princeCharming() { /* ... */ } //她遇到了她的白馬王子
           var unicorn = { /* ... */ },  //帶著他騎著獨(dú)角獸開始周游這個(gè)世界
               dragons = [ /* ... */ ], //與巨龍戰(zhàn)斗
               squirrel = "Hello!";    //巧遇會(huì)說話的松鼠 
    
           adventures.push(unicorn, dragons, squirrel, ....); //還有其他一些新奇的事物
    
       return {    //但是她不得不回到她的王國(guó)里,面對(duì)那些年老的大臣。
    
         story: function() { //她會(huì)經(jīng)常給那些大臣們分享她作為公主最近在外面充滿奇幻的冒險(xiǎn)經(jīng)歷
                    return adventures[adventures.length - 1]; 
            } 
       };          
    }
    
    var littleGirl = princess(); //但是在大臣們的眼里,總是認(rèn)為她只是個(gè)小女孩......
    
    littleGirl.story();  //講的是一些不切實(shí)際,充滿想象的故事
    
    即便所有大臣們知道他們眼前的小女孩是真的公主,但是他們卻不會(huì)相信有巨龍或獨(dú)角獸,
    因?yàn)樗麄冏约簭膩頉]有見到過。大臣們只會(huì)覺得它們只存在于小女孩的想象之中。
    但是我們卻知道小女孩述說的是事實(shí)......
    

定時(shí)器

setTimeout()

  • 作用
    設(shè)置一個(gè)定時(shí)器,在定時(shí)器到期后執(zhí)行一次函數(shù)
  • 語法
    var timerId = setTimeout(執(zhí)行函數(shù) , 延遲毫秒) // 返回一個(gè)整數(shù)作為該延時(shí)操作的ID
  • 清除
    clearTimeout( ID )
  • **setTimeout 0 **
  • 解析:setTimeout(f, 0) 即 將定時(shí)器延遲毫秒設(shè)置為0
  • 作用:間接實(shí)現(xiàn)異步操作
  • 原理:JS的運(yùn)行機(jī)制是逐行執(zhí)行代碼,即單線執(zhí)行
    setTimeout的運(yùn)行機(jī)制是將指定函數(shù)移出本輪執(zhí)行等到下一輪再檢查是否到指定時(shí)間,如果沒到繼續(xù)等待下輪執(zhí)行。
    setTimeout0 表示盡早執(zhí)行而非立刻執(zhí)行 即 把指定函數(shù)放到最后再執(zhí)行

setInterval()

  • 作用
    重復(fù)調(diào)用一個(gè)函數(shù),在每次調(diào)用之間以固定的時(shí)間延遲
  • 語法
    var intervalId = setInterval(執(zhí)行函數(shù) , 延遲毫秒) // 返回一個(gè)整數(shù)作為該延時(shí)操作的ID
  • 清除
    clearInterval( ID )
  • 缺點(diǎn)
    1某些間隔會(huì)被跳過
    2多個(gè)定時(shí)器的代碼執(zhí)行時(shí)間可能會(huì)比預(yù)期小

應(yīng)用

  • **Q:fnArr[ i ] = 10 **
    var fnArr = [];
    for (var i = 0; i < 10; i ++) {
    fnArr[i] = function(){
    return i; ---------------------->i=10 時(shí)滿足跳出條件
    };
    }
    console.log( fnArri ); // 所以總是輸出10
    A: fnArr[ i ] = i
    方法一:隱藏變量 i
    var fnArr = [];
    for (var i = 0; i < 10; i ++) {
    fnArr = (function(){ ---------------------->保存i每次的變量值,防止繼續(xù)循環(huán)
    var n = i;
    return function(){ ----------------------> 暴露i此次的值
    retur n;
    }
    } )(i)
    }
    console.log( fnArr3 ); // 3
    方法二:隱藏整個(gè)函數(shù)
    var fnArr = [];
    for(var i = 0; i < 10; i++){
    (function(n){ ----------------------> 保存整個(gè)函數(shù)
    var n = i;
    fnArr[i] = function(){ ------------> 每次拿到i的變量值 都執(zhí)行一次函數(shù)
    return n;
    }
    })(i)
    }
    console.log( fnArr3 );
  • Q:使用閉包封裝一個(gè)汽車對(duì)象,可以通過如下方式獲取汽車狀態(tài)
    var Car = //todo;
    Car.setSpeed(30);
    Car.getSpeed(); //30
    Car.accelerate();
    Car.getSpeed(); //40;
    Car.decelerate();
    Car.decelerate();
    Car.getSpeed(); //20
    Car.getStatus(); // 'running';
    Car.decelerate();
    Car.decelerate();
    Car.getStatus(); //'stop';//
    Car.speed; //error
    **A: **
    var car=(function(){
    var speed=0;
    function setSpeed(n){ return speed=n; }
    function getSpeed(){ return speed; }
    function accelerate(){ speed +=10; }
    function decelerate(){ speed -=10; }
    function getStatus(){
    if(speed>0){
    return "running";
    } else{
    return "stop";
    }
    }
    return {
    setSpeed: setSpeed,
    getSpeed: getSpeed,
    accelerate: accelerate,
    decelerate: decelerate,
    getStatus: getStatus
    }
    })();

  • Q: 用setTimeout 模擬出setInterval .VS setTimeout

  • 區(qū)別:
    setTimeout重復(fù)運(yùn)行時(shí),延時(shí)= 定時(shí) + 函數(shù)執(zhí)行時(shí)間
    setInterval運(yùn)行時(shí),延時(shí)= 定時(shí),即已經(jīng)包括 函數(shù)執(zhí)行時(shí)間

  • 應(yīng)用:
    setTimeout避免連續(xù)調(diào)用繁復(fù)函數(shù)產(chǎn)生互相干擾的問題
    setInterval要求時(shí)間間隔非常精確

  • 實(shí)現(xiàn):
    var i = 0;
    function intv(){
    setTimeout(function(){
    console.log(i++);
    intv();
    },1000);
    }
    intv();

  • Q: 計(jì)算setTimeout平均最小時(shí)間粒度
    **A: **setTimeout(f, 0)即使將第二個(gè)參數(shù)設(shè)為0,實(shí)際上也達(dá)不到0毫秒。根據(jù)html5標(biāo)準(zhǔn),setTimeout推遲執(zhí)行的時(shí)間最少是4毫秒。如果小于這個(gè)值,會(huì)被自動(dòng)增加到4。這是為了防止多個(gè) setTimeout(f, 0)語句連續(xù)執(zhí)行造成性能問題。

    function getMini(){
         var i = 0; 
         var statr = Date.now(); --------------->先獲取當(dāng)前時(shí)間點(diǎn)
         var clock = setTimeout(function(){ ---->setTimeout(f,0)盡快執(zhí)行函數(shù)
               i++; 
               if(i === 1000){  ---------------->循環(huán)1000次,積累"盡快值"
                      clearTimeout(clock); 
                      var end = Date.now(); ------------>循環(huán)1000次后的時(shí)間點(diǎn)
                      console.log( (end-statr) / i);------->循壞i次所用時(shí)間除以i
               } 
               clock = setTimeout(arguments.callee,0); 
          },0)
    }
    getMini();
    
  • Q: 解析代碼
    **A: **setTineout(f,0)調(diào)整了執(zhí)行順序
    var a = 1;
    setTimeout(function(){
    a = 2;
    console.log(a);--------------->第三次輸出a,a=2
    }, 0);
    var a ;
    console.log(a);--------------->第一次輸出a,a=1
    a = 3;
    console.log(a);--------------->第二次輸出a,a=3
    //輸出 1,3,2

  • Q: 解析代碼
    **A: **死循環(huán)阻塞代碼執(zhí)行,無輸出
    var flag = true; // 定義flag,并賦值為ture
    setTimeout(function(){ // setTimeout(f,0)將函數(shù)放至末尾執(zhí)行
    flag = false;
    },0)
    while(flag){} // 在這里無限循環(huán),后面的代碼都無法執(zhí)行
    console.log(flag); // 所以執(zhí)行不到這里,什么也不會(huì)輸出
  • Q: 解析代碼
    for(var i=0;i<5;i++){
    setTimeout(function(){ // setTimeout(f,0)將函數(shù)放至末尾執(zhí)行
    console.log('delayer:' + i ); // 再執(zhí)行到這里,i=5,所以循環(huán)輸出5個(gè)delayer:5
    }, 0);
    console.log(i); // 所以先執(zhí)行循環(huán),依次輸出01234
    }
    0
    1
    2
    3
    4
    "delayer:5"
    "delayer:5"
    "delayer:5"
    "delayer:5"
    "delayer:5"
    A: 改寫為依次輸出delayer: 0, delayer:1...
    方法一:
    for(var i=0;i<5;i++){
    (function(n){ --------------->保存 整個(gè)函數(shù)的輸出值
    setTimeout(function(){ --------------->函數(shù)放最后執(zhí)行
    console.log('delayer:' + n ); ---------------> 最后遍歷 delayer:i
    }, 0);
    })(i)
    console.log(i); --------------->所以先遍歷i的值
    }
    0
    1
    2
    3
    4
    "delayer:0"
    "delayer:1"
    "delayer:2"
    "delayer:3"
    "delayer:4"
    方法二:
    for(var i=0;i<5;i++){
    setTimeout((function(n){ --------------->保存單次 delayer:i ,并且延后執(zhí)行
    console.log('delayer:' + n );
    })(i), 0);
    console.log(i); --------------->所以先輸出i,再輸出 delayer:i ,然后再次執(zhí)行
    }
    "delayer:0"
    0
    "delayer:1"
    1
    "delayer:2"
    2
    "delayer:3"
    3
    "delayer:4"
    4
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 什么是閉包? 有什么作用閉包:函數(shù)對(duì)象可以通過作用域鏈相互關(guān)聯(lián),函數(shù)體內(nèi)部的變量可以保存在函數(shù)的作用域內(nèi)。 上述代...
    coolheadedY閱讀 754評(píng)論 0 0
  • 1.什么是閉包? 有什么作用? 閉包是指有權(quán)訪問其他函數(shù)作用域中的變量的函數(shù)。 詳細(xì)解釋:就是在一個(gè)函數(shù)中,父函數(shù)...
    Sheldon_Yee閱讀 1,162評(píng)論 2 2
  • 本教程版權(quán)歸小圓和饑人谷所有,轉(zhuǎn)載須說明來源 問題 什么是閉包? 有什么作用閉包(closure)是指有權(quán)訪問另一...
    饑人谷__小圓閱讀 503評(píng)論 0 0
  • 1.什么是閉包? 有什么作用 閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù),也就是定義在函數(shù)內(nèi)部的函數(shù); 函數(shù)retur...
    成熟穩(wěn)重的李先生閱讀 358評(píng)論 0 2