JS閉包、定時(shí)器

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

函數(shù)的作用域scope取決于聲明時(shí)抚恒,而非調(diào)用時(shí)赫编。普通函數(shù)執(zhí)行后函數(shù)體及內(nèi)部變量會(huì)被垃圾清理機(jī)制回收巡蘸,構(gòu)建閉包c(diǎn)losure能讓函數(shù)內(nèi)部的變量駐留在內(nèi)存中并被外部引用。閉包可以理解為保留函數(shù)聲明時(shí)內(nèi)部變量作用域&函數(shù)體構(gòu)成的一個(gè)環(huán)境擂送,通過(guò)這個(gè)環(huán)境能讀取函數(shù)內(nèi)部的變量悦荒。嵌套函數(shù)中子函數(shù)返回構(gòu)成閉包時(shí),返回的內(nèi)容包括該函數(shù)生成時(shí)的變量作用域關(guān)系嘹吨,即環(huán)境變量+函數(shù)搬味。
閉包有兩個(gè)基本特征:1.父函數(shù)嵌套子函數(shù),返回子函數(shù)執(zhí)行結(jié)果蟀拷。2.父函數(shù)作用域的變量被子函數(shù)引用著碰纬。
閉包的實(shí)際作用:1.讀取函數(shù)內(nèi)部的變量;2.內(nèi)存不被釋放问芬,讓變量的值始終保持在內(nèi)存中(但使用不當(dāng)會(huì)引起內(nèi)存泄漏消耗過(guò)多內(nèi)存資源)悦析。
閉包的應(yīng)用場(chǎng)景:1.聲明私有變量。2.隔離作用域此衅。3.構(gòu)建計(jì)數(shù)器强戴。
經(jīng)典錯(cuò)誤案例

var count = []
for (var i = 0; i < 10; i++) {
 count[i] = function() {
 return i
 }
}
console.log(count[0]()) //10
console.log(count[1]()) //10
console.log(count[2]()) //10
console.log(count[3]()) //10

以上代碼count[n]()每次執(zhí)行都返回結(jié)果10。實(shí)際預(yù)期是count[n]() 返回結(jié)果n挡鞍。每次返回10的原因是for循環(huán)運(yùn)行后count[i]數(shù)組中的每個(gè)成員實(shí)際是一個(gè)匿名函數(shù)function(){return i},for循環(huán)執(zhí)行完后i變?yōu)?0骑歹,所以執(zhí)行count[n]()實(shí)際上是執(zhí)行數(shù)組成員中的匿名函數(shù)即(function(){return i}()),i的值已變?yōu)?0所以每次執(zhí)行結(jié)果為10匕累。

改進(jìn)代碼后如下:

var count=[];
for(var i=0;i<10;i++){
count[i]=(function t1(){
var n=i
return function t2(){
return n;
}
}())
}
console.log(count[0]()) //0
console.log(count[1]()) //1
console.log(count[2]()) //2
console.log(count[3]()) //3

count[i]數(shù)組的成員變成了10個(gè)陵刹,由立即執(zhí)行函數(shù)t1即(function t1(){}())的執(zhí)行結(jié)果function t2(){return n}默伍,這里n是由i賦值得來(lái)欢嘿,當(dāng)i循環(huán)遞增時(shí)n隨之改變,由于子函數(shù)t2中變量n始終從父函數(shù)t1的作用域上尋找也糊,所以n始終被保留在內(nèi)存中炼蹦,每次i循環(huán)遞增n就賦值并保留下來(lái)。當(dāng)執(zhí)行count[n]()即執(zhí)行(function t2(){return n}())


2.setTimeout 0 有什么作用

var timerId = setTimeout(func|code, delay)
delay參數(shù)用來(lái)指定在當(dāng)前JS隊(duì)列中的任務(wù)執(zhí)行完后狸剃,延遲多長(zhǎng)時(shí)間將函數(shù)/代碼段添加到任務(wù)隊(duì)列尾部并執(zhí)行掐隐。
以下代碼運(yùn)行后返回一個(gè)整數(shù),表示定時(shí)器的編號(hào),可以用于取消這個(gè)定時(shí)器虑省。但實(shí)際任務(wù)中匿刮,很少這么用。運(yùn)行clock01返回整數(shù)表示定時(shí)器編號(hào)探颈。
var clock01=setTimeout(function(){var ccc=10},5000)

setTimeout(function(){},0)表示指定的任務(wù)在現(xiàn)有的任務(wù)隊(duì)列執(zhí)行完畢之后添加并立即執(zhí)行熟丸。


3.下面的代碼輸出多少?修改代碼讓fnArri 輸出 i伪节。使用兩種以上的方法
var fnArr = [];
    for (var i = 0; i < 10; i ++) {
        fnArr[i] =  function(){
            return i;
        };
    }
    console.log( fnArr[3]() );  //

代碼如下:

方法一:
var fnArr=[];
for (var i=0;i<10;i++){
fnArr[i]=(function() t1{
var n=i;  //t1函數(shù)立即執(zhí)行光羞,不指定傳入?yún)?shù),在父函數(shù)t1中聲明變量n怀大,n由i賦值纱兑,t1執(zhí)行返回函數(shù)t2
return function() t2{
return n //t2函數(shù)執(zhí)行返回n,每次i改變后n會(huì)保留下來(lái)
}
}())
}

方法二:

var fnArr=[];
for (var i=0;i<10;i++){
fnArr[i]=(function t1(n){ //這里參數(shù)n=i化借,相當(dāng)于var n=i
return function t2(){
return n //返回變量n,在父函數(shù)t1的作用域中找到n
}
}(i)) //函數(shù)立即執(zhí)行時(shí)傳入?yún)?shù)i
}

4.使用閉包封裝一個(gè)汽車(chē)對(duì)象潜慎,可以通過(guò)如下方式獲取汽車(chē)狀態(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

代碼如下:

var Car=(function t1(){
 var Speed=0;
 function setSpeed(n){
 Speed=n;
 return Speed;
 }

 function getSpeed(){
 console.log(Speed)
 return Speed;
 }

 function accelerate(){
 Speed+=10;
 return Speed;
 }

 function decelerate(){
 Speed-=10;
 return Speed;
 }

 function getStatus(){
 if(Speed>0){
 console.log("running");
 } else if(Speed===0){
 console.log("stop");
 }
 }

 function error(){
 console.log("error:");
 }

 return {
 "setSpeed":setSpeed,
 "getSpeed":getSpeed,
 "accelerate":accelerate,
 "decelerate":decelerate,
 "getStatus":getStatus,
 "speed":error()
 }
}())

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

5.寫(xiě)一個(gè)函數(shù)使用setTimeout模擬setInterval的功能
var i=0; //聲明初始值由0開(kāi)始
(function intv(){
 setTimeout(function(){//setTimeout(函數(shù),時(shí)間間隔)
 console.log(i++)
 intv();//由于setTimeout只執(zhí)行一次,所以用遞歸調(diào)用自身做循環(huán)
 },1000)
})()

6.寫(xiě)一個(gè)函數(shù)蓖康,計(jì)算setTimeout平均最小時(shí)間粒度
function mintime(){
var i=0;
var starttime=Date.now();
var timer=setTimeout(function t1(){
i++;
if(i===1000){
clearTimeout(timer);
var endtime=Date.now();
console.log((endtime-starttime)/i)
} else{
timer=setTimeout(t1.arguments.callee,0);
}
},0)
}
mintime();

7.下面這段代碼輸出結(jié)果是? 為什么?
var a = 1;
setTimeout(function(){
 a = 2;
 console.log(a);
}, 0);
var a ;
console.log(a);
a = 3;
console.log(a);

setTimeout會(huì)將其參數(shù)1中的函數(shù)或代碼段在JS任務(wù)隊(duì)列中的當(dāng)前任務(wù)執(zhí)行完后勘纯,延遲指定的時(shí)間放入隊(duì)列尾部執(zhí)行。
以上分析執(zhí)行過(guò)程為

var a=1;
var a;
console.log(a); //返回1
a=3;
console.log(a); //返回3
setTimeout(function(){
a=2;
console.log(a); //放回2
},0)

8.下面這段代碼輸出結(jié)果是? 為什么?
var flag = true;
setTimeout(function(){
 flag = false;
},0)
while(flag){}
console.log(flag);

以上代碼分析如下:

var flag=true;
while(flag){}//while循環(huán)條件成立钓瞭,由于沒(méi)有break/continue會(huì)一直執(zhí)行
console.log(flag);
setTimeout(function(){//待當(dāng)前隊(duì)列中所有任務(wù)執(zhí)行完后驳遵,延遲0毫秒將函數(shù)放入隊(duì)列尾部執(zhí)行
flag=false;
},0)

實(shí)際執(zhí)行結(jié)果是不顯示任何內(nèi)容,while循環(huán)條件成立沒(méi)有break/continue終止山涡,會(huì)一直執(zhí)行循環(huán)堤结,后面的console.log無(wú)法執(zhí)行


9.下面這段代碼輸出?如何輸出delayer: 0, delayer:1...(使用閉包來(lái)實(shí)現(xiàn))
for(var i=0;i<5;i++){
 setTimeout(function(){
 console.log('delayer:' + i );
 }, 0);
 console.log(i);
}

實(shí)現(xiàn)如下:

//方法一
for(var i=0;i<5;i++){
 setTimeout((function t1() {
 var m=i; //父函數(shù)t1下聲明變量m鸭丛,m的值由for循環(huán)中的作用域變量i賦值得來(lái)竞穷,m保留每次i遞增后的值
 return function t2() {
 console.log('delayer:' + m )
 }
 }()),0);
 console.log(i);
}

//方法二:寫(xiě)法與方法一類(lèi)似,只不過(guò)t1()立即執(zhí)行函數(shù)執(zhí)行時(shí)傳入實(shí)際參數(shù)i賦值給t1定義的形式參數(shù)m
for(var i=0;i<5;i++){
 setTimeout((function t1(m){//定義函數(shù)t1形式參數(shù)m鳞溉,m由t1函數(shù)立即執(zhí)行時(shí)傳入的實(shí)際參數(shù)i賦值得來(lái)瘾带,相當(dāng)于在t1函數(shù)中var m=i;
 return function t2(){
 console.log('delayer:' + m )
 }
 }(i)),0);//t1函數(shù)立即執(zhí)行時(shí)傳入實(shí)際參數(shù)i
 console.log(i);
}

//方法三:父函數(shù)t1是立即執(zhí)行函數(shù),t1下聲明變量m熟菲,m的值由for循環(huán)的作用域變量i賦值得來(lái)看政。m保留每次i遞增后的值
for(var i=0;i<5;i++){
 (function t1(){
 var m=i
 return (setTimeout(function t2(){
 console.log('delayer:'+m)
 },0))
 }())
 console.log(i)
}

//方法四:
for(var i=0;i<5;i++){
 (function t1(m){//父函數(shù)t1定義形式參數(shù)m,m的值有t1函數(shù)立即執(zhí)行時(shí)傳入的實(shí)際參數(shù)i賦值得來(lái)抄罕,相當(dāng)于在t1函數(shù)中var m=i
 return setTimeout(function t2(){
 console.log('delayer:'+m)
 },0)
 }(i)) //t1立即執(zhí)行函數(shù)傳入實(shí)際參數(shù)i
 console.log(i)
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末允蚣,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子呆贿,更是在濱河造成了極大的恐慌嚷兔,老刑警劉巖森渐,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異冒晰,居然都是意外死亡同衣,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)壶运,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)乳怎,“玉大人,你說(shuō)我怎么就攤上這事前弯◎阶海” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵恕出,是天一觀的道長(zhǎng)询枚。 經(jīng)常有香客問(wèn)我,道長(zhǎng)浙巫,這世上最難降的妖魔是什么金蜀? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮的畴,結(jié)果婚禮上渊抄,老公的妹妹穿的比我還像新娘。我一直安慰自己丧裁,他們只是感情好护桦,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著煎娇,像睡著了一般二庵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上缓呛,一...
    開(kāi)封第一講書(shū)人閱讀 51,443評(píng)論 1 302
  • 那天催享,我揣著相機(jī)與錄音,去河邊找鬼哟绊。 笑死因妙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的票髓。 我是一名探鬼主播攀涵,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼炬称!你這毒婦竟也來(lái)了汁果?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤玲躯,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體跷车,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡棘利,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了朽缴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片善玫。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖密强,靈堂內(nèi)的尸體忽然破棺而出茅郎,到底是詐尸還是另有隱情,我是刑警寧澤或渤,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布系冗,位于F島的核電站,受9級(jí)特大地震影響薪鹦,放射性物質(zhì)發(fā)生泄漏掌敬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一池磁、第九天 我趴在偏房一處隱蔽的房頂上張望奔害。 院中可真熱鬧,春花似錦地熄、人聲如沸华临。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)银舱。三九已至,卻和暖如春跛梗,著一層夾襖步出監(jiān)牢的瞬間寻馏,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工核偿, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留诚欠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓漾岳,卻偏偏與公主長(zhǎng)得像轰绵,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子尼荆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

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

  • 什么是閉包? 有什么作用閉包:函數(shù)對(duì)象可以通過(guò)作用域鏈相互關(guān)聯(lián)捅儒,函數(shù)體內(nèi)部的變量可以保存在函數(shù)的作用域內(nèi)液样。 上述代...
    coolheadedY閱讀 727評(píng)論 0 0
  • 一鞭莽、問(wèn)題 (一)坊秸、什么是閉包? 有什么作用 閉包是指能夠訪問(wèn)自由變量的函數(shù) (變量在本地使用,但在閉包中定義)澎怒。換...
    該帳號(hào)已被查封_才怪閱讀 397評(píng)論 0 1
  • 什么是閉包? 有什么作用? 閉包是指有權(quán)限訪問(wèn)另一個(gè)函數(shù)作用域的變量的函數(shù)(就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù))喷面。...
    _fin閱讀 687評(píng)論 0 3
  • 問(wèn)題 什么是閉包? 有什么作用 閉包可以用來(lái)讀取函數(shù)內(nèi)部的變量惧辈。 由于作用域鏈表琳状,外部是無(wú)法讀取到函數(shù)內(nèi)部的變量的...
    不是魷魚(yú)閱讀 390評(píng)論 0 0
  • 今天的由于身體的原因,我的感覺(jué)陷入到混沌狀態(tài)咬像,頭腦一直處于空白的狀態(tài)算撮,思維能力急速下降,理解力也脫節(jié)县昂,剛才孩子和我...
    尚巾林閱讀 206評(píng)論 0 0