閉包、定時(shí)器

1.什么是閉包? 有什么作用

  • 定義:閉包就是嵌套在函數(shù)里面的內(nèi)部函數(shù),并且該內(nèi)部函數(shù)可以訪問外部函數(shù)中聲明的所有局部變量矩桂,參數(shù)和其他內(nèi)部的函數(shù)。當(dāng)該內(nèi)部函數(shù)在外部函數(shù)外被調(diào)用了痪伦,就生成了閉包侄榴。
(1)閉包包括了函數(shù)和創(chuàng)建該函數(shù)的環(huán)境。這個(gè)環(huán)境由閉包創(chuàng)建時(shí)在作用域中的任何局部變量組成流妻。
(2)[函數(shù)]和[函數(shù)內(nèi)部能訪問到的變量(也叫環(huán)境)]的總和牲蜀,就是一個(gè)閉包。
(3)閉包不是非得return回一個(gè)函數(shù)绅这,return出一個(gè)對象也可以是閉包涣达。
(4)特點(diǎn)一:作為一個(gè)函數(shù)變量的一個(gè)引用,當(dāng)函數(shù)返回時(shí)证薇,其處于激活的狀態(tài)度苔。
(5)特點(diǎn)二:一個(gè)閉包就是當(dāng)一個(gè)函數(shù)返回的時(shí)候,一個(gè)沒有釋放資源的棧區(qū)浑度。
  • 作用:
    (1)保存變量現(xiàn)場,封裝私有變量
    (2)隔離作用域寇窑;
    (3)做計(jì)時(shí)器
    (4)獨(dú)立了一個(gè)命名空間
    (5)閉包會使變量駐留內(nèi)存,不會被自動(dòng)清除箩张;使用時(shí)應(yīng)謹(jǐn)慎甩骏,以免引起內(nèi)存泄露,影響性能
閉包的缺點(diǎn):
1.在IE9之前因?yàn)槭褂貌煌睦占瘷C(jī)制會導(dǎo)致循環(huán)引用會造成內(nèi)存泄漏
2. 閉包會攜帶包含函數(shù)的作用域先慷,所以會比其他函數(shù)占用更多內(nèi)存饮笛,過度使用閉包會造成內(nèi)存占用過多
例子:
在函數(shù)內(nèi)部讀取它的內(nèi)部變量:
function fn(){
    var a = 1;
    function add(){
      a+=1
      console.log(a)
    }
    return add //add就是一個(gè)橋梁
}
var r = fn() //閉包论熙,包括函數(shù)add和包含變量a的環(huán)境
r() //輸出2  
//add被賦給了一個(gè)全局變量r福青,導(dǎo)致add一直存在內(nèi)存中,而add的存在依賴于a,所以a也始終在內(nèi)存中无午。
因此:閉包就是一個(gè)橋梁媒役,連接著外部環(huán)境和父函數(shù)的局部變量
經(jīng)典例子:
(function(){
   var arr = []
   for(var i=0;i<5;i++){
        arr[i] = function(){
          console.log(i)
        }
  }
  arr[1]()
})()
為什么!O艹佟酣衷!無論arr[1]-arr[5]()輸出結(jié)果都是5,因?yàn)樗L問的i是外部函數(shù)的踩验,
當(dāng)循環(huán)完以后鸥诽,i已經(jīng)累加到5了商玫,所以無論多少輸出當(dāng)然是5(作用域鏈)

利用閉包特點(diǎn)可以保存變量現(xiàn)場(讓變量不被釋放):
(function() { 
    var arr = [] 
    for (var i = 0; i < 5; i++) { 
          arr[i] = function (n) { 
            return function () { 
                      alert(n) 
            } 
          }(i) //把i傳遞進(jìn)來箕憾,作用域中就可以取到當(dāng)前的i
     } 
     arr[1]()//輸出當(dāng)前值1
})()

2.setTimeout 0 有什么作用

  • 定義:setTimeout函數(shù)用來指定某個(gè)函數(shù)或某段代碼,在多少毫秒之后執(zhí)行拳昌。它返回一個(gè)整數(shù)袭异,表示定時(shí)器的編號,以后可以用來取消這個(gè)定時(shí)器炬藤。

(1)setTimeout(func|code, delay)御铃,一般接受兩個(gè)參數(shù),第一個(gè)參數(shù)可以是字符串沈矿,也可以是func函數(shù)名上真,第二個(gè)參數(shù)是設(shè)置多少毫秒ms后執(zhí)行代碼.如果省略第二個(gè)參數(shù),該參數(shù)默認(rèn)為0.
(2)setTimeout(“code”,millisec):執(zhí)行代碼必須用字符串形式羹膳,millise設(shè)置在多少毫秒之后執(zhí)行這段代碼睡互。
eg:setTimeout('console.log(2)',1000); //1000ms后控制臺輸出2
(3)setTimeout(fn,millisec)中fn為函數(shù)名,也可以是回調(diào)函數(shù)陵像,但記得是寫函數(shù)名而不是寫調(diào)用函數(shù)就珠,millise設(shè)置在多少毫秒之后執(zhí)行這個(gè)函數(shù)。
(4)可以把setTimeout中的函數(shù)排在任務(wù)隊(duì)列末尾醒颖,等待其他語句全部執(zhí)行完畢再開始立即執(zhí)行,可以理解為setTimeout優(yōu)先級在任務(wù)隊(duì)列中最低

setTimeout (‘console.log(最尾)'妻怎,0)//代表當(dāng)前任務(wù)隊(duì)列執(zhí)行后才立刻執(zhí)行輸出“最尾”
setTimeout('console.log(最尾了但還要1000ms后再執(zhí)行)',1000)//代表當(dāng)前任務(wù)隊(duì)列執(zhí)行后再過1000ms才執(zhí)行
例子:
setTimeout('console.log(1)',1000); //右邊為執(zhí)行順序:  2
console.log(2); //                                  3
console.log(3); //                                  4
setTimeout('console.log(4)',0);//                   1

總結(jié):setTimeout 0 有什么作用泞歉?(任務(wù)隊(duì)列中優(yōu)先級最低)
可以讓setTimeout中的函數(shù)排在任務(wù)隊(duì)列末尾逼侦,等待其他語句全部執(zhí)行完畢再開始立即執(zhí)行


3.下面的代碼輸出多少腰耙?修改代碼讓fnArr1輸出 i榛丢。使用兩種以上的方法

題目:
var fnArr = []; 
for (var i = 0; i < 10; i ++) { 
        fnArr[i] = function(){ 
            return i; 
      }; 
} 
console.log( fnArr[3]() );~~~無奈都是輸出10,因?yàn)閳?zhí)行函數(shù)之前 i=10沟优;在for循環(huán)中涕滋,
i的值并沒有隨著循環(huán)保存在函數(shù)中,所以關(guān)鍵在于如何保存i的值挠阁,所以有了以下閉包的方法:
——————————————————————————————————————————————————————
//使用閉包函數(shù)實(shí)現(xiàn)返回當(dāng)前傳入的值
方法一:
var fnArr = []; 
for(var i = 0;i < 10;i++) { 
     fnArr[i] = function(n){//用數(shù)組記錄下循環(huán)中每次的值
         return function(){
           return n;
         }
     }(i) //每次循環(huán)傳入實(shí)參i
} 
console.log( fnArr[3]() );//輸出3
————————————————————————————————————————————————————
方法二:
function func(val) { 
    return function() { 
          return val;
       }; 
  } 
var fnArr = []; 
for (var i = 0; i < 10; i++) {
 //將返回值(i)存入fnArr數(shù)組 
      fnArr[i] = func(i); 
} 
console.log(fnArr[3]());//輸出3
——————————————————————————————————————————————————————
方法三:
var fnArr = []; 
for(var i=0;i<10;i++){
  (function(n){
    fnArr[n] = function(){
      return n
    }
  })(i)//將i作為實(shí)參傳入立即執(zhí)行函數(shù)
}
console.log( fnArr[3]());//輸出3

4.使用閉包封裝一個(gè)汽車對象宾肺,可以通過如下方式獲取汽車狀態(tài)

解答:
var Car = (function(){
    var val;
    function setSpeed(val){
      speed = val;
    }
    function getSpeed(){
      return speed;
    }
    function accelerate(){
      speed +=10;
    }
    function decelerate(){
      speed -=10;
    }
    function getStatus(){
      if(speed===0){
        return "stop";
      }else{
        return "running";
      }
    }
    return {
      "setSpeed":setSpeed,
      "getSpeed":getSpeed,
      "accelerate":accelerate,
      "decelerate":decelerate,
      "getStatus":getStatus
    };
}());

// var Car = //todo;
console.log(Car.setSpeed(30));
console.log(Car.getSpeed()); //30
console.log(Car.accelerate());
console.log(Car.getSpeed()); //40;
console.log(Car.decelerate());
console.log(Car.decelerate());
console.log(Car.getSpeed()); //20
console.log(Car.getStatus()); // 'running';
console.log(Car.decelerate()); 
console.log(Car.decelerate());
console.log(Car.getStatus()); //'stop';
//Car.speed; //error

5.寫一個(gè)函數(shù)使用setTimeout模擬setInterval的功能

方法一:
var i = 0;
function fn(){
    setTimeout(function(){
        console.log(i);
        i++
        fn()  //遞歸不斷調(diào)用自己
    },2000);
}
fn()
方法二:
var i = 0;
function fn(){
      var clock = setTimeout(function(){
      console.log(i);
      i++
      clock = setTimeout(arguments.callee,2000); //遞歸不斷調(diào)用自己
    },2000);
}
fn()
每隔兩秒輸出:0,1,2,3,4,5,6~~~~~~~~~~
需要注意的是溯饵,使用setTimeout并不能完全模擬出setInterval的功能。
在問答1中介紹過锨用,setTimeout是等前面的任務(wù)執(zhí)行之后再開始計(jì)算時(shí)間間隔丰刊。
而setInterval函數(shù)指定的是“開始執(zhí)行”之間的間隔,并不考慮每次任務(wù)執(zhí)行本身所消耗的時(shí)間增拥,比如啄巧,
setInterval指定每100ms執(zhí)行一次,每次執(zhí)行需要5ms掌栅,那么第一次執(zhí)行結(jié)束后95毫秒秩仆,第二次執(zhí)行就會
開始。如果某次執(zhí)行耗時(shí)特別長猾封,比如需要105毫秒澄耍,那么它結(jié)束后,下一次執(zhí)行就會立即開始晌缘。

6.寫一個(gè)函數(shù)齐莲,計(jì)算setTimeout平均[備注:新加]最小時(shí)間粒度(參考)

function getMini() { 
    var t1 = Date.now(); //開始執(zhí)行函數(shù)的時(shí)間 
    var i = 0; 
    var clock = setTimeout(function(){ 
          i++; 
      if(i === 1000) { //計(jì)算1000次 
          clearTimeout(clock); //停止 
          var t2 = Date.now(); //結(jié)束時(shí)間 
          console.log((t2-t1)/i); //平均值得到最小粒度 
      }else{

      //arguments 當(dāng)前函數(shù)的參數(shù)"數(shù)組" 
      //arguments.callee 調(diào)用這個(gè)"數(shù)組"的函數(shù),也就是當(dāng)前函數(shù) 
      //相當(dāng)于循環(huán)執(zhí)行當(dāng)前函數(shù), 直到 i 滿足判斷語句中的值 
          clock = setTimeout(arguments.callee,0);
      } 
  },0) //此處一定是0,無間隔地循環(huán)執(zhí)行直到到1000為止
}
getMini()
輸出大約4.249秒~4.5秒

7.下面這段代碼輸出結(jié)果是? 為什么?

題目:
var a = 1;
setTimeout(function(){
     a = 2; 
     console.log(a);
}, 0);
var a ;
console.log(a);
a = 3;
console.log(a);

結(jié)果:
2.png

setTimeout的delay值被設(shè)為0磷箕,也就意味著里面的函數(shù)要等待其他語句全部執(zhí)行完畢才會運(yùn)行选酗,setTimeout( ,0),代碼會在最后執(zhí)行。所以最先輸出的是1岳枷,然后賦值3給a,最后執(zhí)行setTimeout()里面的代碼


8.下面這段代碼輸出結(jié)果是? 為什么?

題目:
var flag = true;
setTimeout(function(){ 
    flag = false;
},0)
while(flag){}
console.log(flag);
------------------
等同于
var flag = true芒填;
while(flag){}  //一直卡在這位置不往下執(zhí)行了
console.log(flag)
setTimeout(function(){
    flag = false
})

分析:只要while(expression){statement},ex條件一直為true,那么就不斷循環(huán)執(zhí)行st嫩舟,setTimeout的delay值被設(shè)為0氢烘,也就意味著里面的函數(shù)要等待其他語句全部執(zhí)行完畢才開始再運(yùn)行。而while(flag){}因?yàn)閒lag為true的關(guān)系永遠(yuǎn)不會停止家厌,所以console.log(flag)也就永遠(yuǎn)不會執(zhí)行播玖。


9.下面這段代碼輸出?如何輸出delayer: 0, delayer:1...(使用閉包來實(shí)現(xiàn))

題目:
for(var i=0;i<5;i++){ 
    setTimeout(function(){ 
      console.log('delayer:' + i ); 
    }, 0); 
   console.log(i);
}
結(jié)果3.png

解答:用閉包保存循環(huán)中每次的i值饭于,閉包只是起到保存變量的作用蜀踏,并未因此改變setTimeout( ,0)中的代碼依然放置事件隊(duì)列最尾處才執(zhí)行的規(guī)則

方法一:
for(var i=0;i<5;i++){ 
  (function(n){
     setTimeout(function(){      
       console.log('delayer:' + n ); 
    }, 0);
  })(i)
   console.log(i);
}

結(jié)果3.png

方法二:
for(var i=0;i<5;i++){ 
    setTimeout((function(n){ 
      return function(){
         console.log('delayer:' + n ); 
      }
    })(i), 0); 
   console.log(i);
}

結(jié)果3.png

方法三:
for(var i=0;i<5;i++){
  fn = function(n){
    return function(){
      console.log("delayer:"+n)
    }
  }(i)
  setTimeout(fn,0)
  console.log(i)
}
結(jié)果3.png

本文版權(quán)歸本人和饑人谷所有,轉(zhuǎn)載請注明來源掰吕。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末果覆,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子殖熟,更是在濱河造成了極大的恐慌局待,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異钳榨,居然都是意外死亡舰罚,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進(jìn)店門薛耻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來营罢,“玉大人,你說我怎么就攤上這事饼齿∷茄” “怎么了?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵缕溉,是天一觀的道長考传。 經(jīng)常有香客問我,道長倒淫,這世上最難降的妖魔是什么伙菊? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮敌土,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘运翼。我一直安慰自己返干,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布血淌。 她就那樣靜靜地躺著矩欠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪悠夯。 梳的紋絲不亂的頭發(fā)上癌淮,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天,我揣著相機(jī)與錄音沦补,去河邊找鬼乳蓄。 笑死,一個(gè)胖子當(dāng)著我的面吹牛夕膀,可吹牛的內(nèi)容都是我干的虚倒。 我是一名探鬼主播,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼产舞,長吁一口氣:“原來是場噩夢啊……” “哼魂奥!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起易猫,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤耻煤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體哈蝇,經(jīng)...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嘴办,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了买鸽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涧郊。...
    茶點(diǎn)故事閱讀 38,643評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖眼五,靈堂內(nèi)的尸體忽然破棺而出妆艘,到底是詐尸還是另有隱情,我是刑警寧澤看幼,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布批旺,位于F島的核電站,受9級特大地震影響诵姜,放射性物質(zhì)發(fā)生泄漏汽煮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一棚唆、第九天 我趴在偏房一處隱蔽的房頂上張望暇赤。 院中可真熱鬧,春花似錦宵凌、人聲如沸鞋囊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽溜腐。三九已至,卻和暖如春瓜喇,著一層夾襖步出監(jiān)牢的瞬間挺益,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工乘寒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留望众,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓肃续,卻偏偏與公主長得像黍檩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子始锚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評論 2 348

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

  • 問題 一瞧捌、什么是閉包? 有什么作用棵里? 閉包閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)润文。在javascript中,只有函...
    婷樓沐熙閱讀 576評論 0 0
  • 什么是閉包? 有什么作用閉包:函數(shù)對象可以通過作用域鏈相互關(guān)聯(lián),函數(shù)體內(nèi)部的變量可以保存在函數(shù)的作用域內(nèi)头谜。 上述代...
    coolheadedY閱讀 725評論 0 0
  • 本教程版權(quán)歸小圓和饑人谷所有柱告,轉(zhuǎn)載須說明來源 問題 什么是閉包? 有什么作用閉包(closure)是指有權(quán)訪問另一...
    饑人谷__小圓閱讀 491評論 0 0
  • 1.什么是閉包? 有什么作用 閉包指有權(quán)訪問另一個(gè)函數(shù)作用域的變量的函數(shù)际度。創(chuàng)建閉包的常見方式 是 在一個(gè)函數(shù)...
    JunVincetHuo閱讀 1,371評論 0 2
  • 我終于認(rèn)識到葵袭,或者說相信,在我們中一些人看來乖菱,自由和安靜對自己是必須爭取和維護(hù)的特權(quán)坡锡,對他人則是個(gè)無關(guān)緊要的名詞,...
    馬丁路子閱讀 392評論 0 1